Streambuilder 渲染两次,第一次使用初始数据,第二次使用正确数据,但小部件不更新

Posted

技术标签:

【中文标题】Streambuilder 渲染两次,第一次使用初始数据,第二次使用正确数据,但小部件不更新【英文标题】:Streambuilder render twice, the first time with initial data the second one with correct data, but the widget doesn't update 【发布时间】:2020-10-20 05:21:59 【问题描述】:

您好,我是 Flutter 的新手,我在状态方面遇到了麻烦。我正在使用 Bloc 来处理状态,但是当我尝试在第一个视图中设置状态时,我无法在第二个视图中显示它。

在第二个视图中,streambuilder 首先显示初始值和连接等待,然后获取正确的数据并将状态更改为活动,但小部件不更新。

第一次查看

class LoginScreen extends StatefulWidget 
  LoginScreen(Key key, this.title) : super(key: key);

  final String title;

  @override
  _LoginScreenState createState() => _LoginScreenState();


class _LoginScreenState extends State<LoginScreen> 


  Widget _divider() 
    return Container(
      margin: EdgeInsets.symmetric(vertical: 10),
      child: Row(
        children: <Widget>[
          SizedBox(
            width: 30,
          ),
          Expanded(
            child: Padding(
              padding: EdgeInsets.symmetric(horizontal: 30),
              child: Divider(
                thickness: 1,
              ),
            ),
          ),
          SizedBox(
            width: 20,
          ),
        ],
      ),
    );
  

  final _emailController = TextEditingController();
  final _passwordController =TextEditingController();

  _updateEmail (String text) => userBloc.updateUser(text);


  Widget _emailPasswordWidget() 
    return Column(
      children: <Widget>[
        EntryFields("Ingresa tu email", value:_emailController, onchanged: _updateEmail),
        EntryFields("Ingresa tu Password",value: _passwordController, isPassword: true),
      ],
    );
  

  _goSignupScreen() async  return Navigator.pushNamed(context, Routes.signupRoute ); 
  _goHomeScreen() async  
      _updateEmail(_emailController.text);
      return Navigator.pushNamed(context, Routes.homeRoute );
    


  @override
  Widget build(BuildContext context) 
    final height = MediaQuery.of(context).size.height;
    return Scaffold(
        body: Container(
      height: height,
      child: Stack(
        children: <Widget>[
          Positioned(
              top: -height * .15,
              right: -MediaQuery.of(context).size.width * .4,
              child: BezierContainer()),
          Container(
            padding: EdgeInsets.symmetric(horizontal: 20),
            child: SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  SizedBox(height: height* 0.2),
                  Center(child:  AppIconWidget(image: 'assets/images/logo.png', scale: 0.1)),
                  SizedBox(height: 50),
                  _emailPasswordWidget(),
                  SizedBox(height: 20),
                  SubmitButton(title: 'Iniciar Sesión', onPressed: _goHomeScreen ),
                  Container(
                    padding: EdgeInsets.symmetric(vertical: 10),
                    alignment: Alignment.centerRight,
                    child: Text('Olvidaste tu contraseña?',
                        style: TextStyle(
                            fontSize: 14, fontWeight: FontWeight.w500)),
                  ),
                  _divider(),
                  FacebookSignInButton(onPressed: ()),
                  GoogleSignInButton(onPressed: () ),
                  SizedBox(height: height * .055),
                  AccountLabel(label: 'No tienes cuenta?', btnText: 'Registrate', onPressed: _goSignupScreen ),
                ],
              ),
            ),
          ),
          // Positioned(top: 40, left: 0, child: BackBtn() ),
        ],
      ),
    ),);
  

集团

class UserBloc 
  final _userRepository = UserRepository();
  final _userController = PublishSubject();;

  Observable get getUser => _userController.stream;
  

  updateUser(user) async 
      var currentUser = await _userRepository.updateCurrentUser(user);
    _userController.sink.add(currentUser);
    return null;
  

  
   dispose() 
    _userController.close();
  


final userBloc = UserBloc();

提供者

class UserProvider 

   Future updateCurrentUser(user) async
     return currentUser = user;
   

存储库

class UserRepository 
  final userProvider = UserProvider();

  Future updateCurrentUser(user) 
    return userProvider.updateCurrentUser(user);
    

第二次查看

class HomeScreen extends StatelessWidget 
  
 @override
  Widget build(BuildContext context) 

    return Scaffold(
      appBar: AppBar(title: Text('title')),
      body: StreamBuilder(
        initialData: 'here!',
        stream: userBloc.getUser,
        builder: (BuildContext context, AsyncSnapshot snapshot) 
          print('...');
          print('$snapshot.data');
          print('$snapshot.connectionState');
          print('...');
          if (snapshot.hasError) return Text('Error: $snapshot.error');
          switch (snapshot.connectionState) 
            case ConnectionState.none:
              return Text('Select lot');
            case ConnectionState.waiting:
              return Text('Awaiting bids...');
            case ConnectionState.active:
              return Text('\$$snapshot.data');
            case ConnectionState.done:
              return Text('\$$snapshot.data (closed)');
          
          return null; // unreachable
        ,
      ),
    );
  

谢谢!

【问题讨论】:

【参考方案1】:

我认为最好使用 snapshot.hasData,而不是为 connectionState 进行切换

if (snapshot.hasData) 
   return Text('\$$snapshot.data');
 else 
   return Text('Loading;);

【讨论】:

我做到了,但它只渲染初始数据(这里!),但是当我看到日志时,我看到状态正在更新 I/flutter (21519): ... I/扑动(21519):在这里! I/flutter (21519): ConnectionState.waiting I/flutter (21519): ... I/flutter (21519): ... I/flutter (21519): 12345 I/flutter (21519): ConnectionState.active I/颤动(21519):...

以上是关于Streambuilder 渲染两次,第一次使用初始数据,第二次使用正确数据,但小部件不更新的主要内容,如果未能解决你的问题,请参考以下文章

在 StreamBuilder 中渲染 ListView.ListTile

函数式组件渲染一次,类组件渲染两次

导航到该组件后反应功能组件渲染两次,但是在尝试刷新时,仅渲染一次

cocos2d-x ios游戏开发初认识 渲染的优化

构造函数和渲染方法在反应组件中运行两次

Flutter 中的 StreamBuilder 卡在 ConnectionState.waiting 中并且只显示加载标记