【问题标题】:Error when I try to show a SnackBar in a builder当我尝试在构建器中显示 SnackBar 时出错
【发布时间】:2019-03-21 19:23:30
【问题描述】:

这是我的main.dart

class MyApp extends StatelessWidget {
var login = new Login("xxxxxxx", "xxxxxxxxx");
final scaffoldKey = new GlobalKey<ScaffoldState>();

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Fetch Data Example',
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: Scaffold(
      key: scaffoldKey,
      appBar: AppBar(
        title: Text('Fetch Data Example'),
      ),
      body: Center(
        child: FutureBuilder(
          future: login.main(),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              _showSnackBar(snapshot.data);
              return new Container();
            } else if (snapshot.hasError) {
              return Text("${snapshot.error}");
            }
            // By default, show a loading spinner
            return CircularProgressIndicator();
          },
        ),
      ),
    ),
  );
}

_showSnackBar(var text) {
  scaffoldKey.currentState
      .showSnackBar(new SnackBar(content: new Text(text)));
}
}

我想在 FutureBuilder 中显示一个 SnackBar,但是当我运行时,出现了错误:

I/flutter ( 8918): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY 
╞═══════════════════════════════════════════════════════════
I/flutter ( 8918): The following assertion was thrown building 
FutureBuilder<dynamic>(dirty, state:
I/flutter ( 8918): _FutureBuilderState<dynamic>#9d4af):
I/flutter ( 8918): setState() or markNeedsBuild() called during build.
I/flutter ( 8918): This Scaffold widget cannot be marked as needing to 
build because the framework is already in the
I/flutter ( 8918): process of building widgets. A widget can be marked 
as needing to be built during the build phase
I/flutter ( 8918): only if one of its ancestors is currently building. 
This exception is allowed because the framework
I/flutter ( 8918): builds parent widgets before children, which means 
a dirty descendant will always be built.
I/flutter ( 8918): Otherwise, the framework might not visit this 
widget during this build phase.
I/flutter ( 8918): The widget on which setState() or markNeedsBuild() 
was called was:
I/flutter ( 8918):   Scaffold-[LabeledGlobalKey<ScaffoldState>#e4a3f] 
(state: ScaffoldState#cf908(tickers: tracking 2
I/flutter ( 8918):   tickers))
I/flutter ( 8918): The widget which was currently being built when the 
offending call was made was:
I/flutter ( 8918):   FutureBuilder<dynamic>(dirty, state: _ 
FutureBuilderState<dynamic>#9d4af)
I/flutter ( 8918): 
I/flutter ( 8918): When the exception was thrown, this was the stack:
I/flutter ( 8918): #0      Element.markNeedsBuild.<anonymous closure> 
(package:flutter/src/widgets/framework.dart:3472:11)
I/flutter ( 8918): #1      Element.markNeedsBuild 
(package:flutter/src/widgets/framework.dart:3498:6)
I/flutter ( 8918): #2      State.setState 
(package:flutter/src/widgets/framework.dart:1141:14)
I/flutter ( 8918): #3      ScaffoldState.showSnackBar 
(package:flutter/src/material/scaffold.dart:1110:5)
I/flutter ( 8918): #4      MyApp._showSnackBar 
I/flutter ( 8918): #5      MyApp.build.<anonymous closure> 
I/flutter ( 8918): #6      _FutureBuilderState.build 
(package:flutter/src/widgets/async.dart)
I/flutter ( 8918): #7      StatefulElement.build 
(package:flutter/src/widgets/framework.dart:3766:27)
I/flutter ( 8918): #8      ComponentElement.performRebuild 
(package:flutter/src/widgets/framework.dart:3678:15)
I/flutter ( 8918): #9      Element.rebuild 
(package:flutter/src/widgets/framework.dart:3531:5)
I/flutter ( 8918): #10     BuildOwner.buildScope 
(package:flutter/src/widgets/framework.dart:2273:33)
I/flutter ( 8918): #11     

我的目的是它在获取数据之前显示一个加载微调器,一旦获取数据,它就会显示一个 SnackBar。那么我应该怎么做才能解决这个问题呢? Login.main 是一个从数据库获取数据的函数。

【问题讨论】:

    标签: flutter flutter-animation


    【解决方案1】:

    FutureBuilderStreamBuilder 应该只用于构建小部件,而不是执行导航等逻辑。改用initStatelike

    @override
    void initState() {
      super.initState();
      _checkLogin();
    }
    
    Future<void> _checkLogin() async {
      try {
        var data = await login.main();
        setState(() {
          _waitForLogin = false;
        });
        _showSnackBar(data);
      } catch(e) {
        setState(() {
          _waitForLogin = false;
          _loginErrorMessage = '$e';
        });
      }
    }
    
    bool _isWaitForLogin = true;
    String _loginErrorMessage;
    
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Fetch Data Example',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: Scaffold(
          key: scaffoldKey,
          appBar: AppBar(
            title: Text('Fetch Data Example'),
          ),
          body: Center(
            child: _isWaitForLogin ? CircularProgressIndicator() : 
              (_loginErrorMessage != null ? Text(_loginErrorMessage : Container())
          ),
        ),
      );
    }
    

    【讨论】:

    • (_loginErrorMessage != null ? Text(_loginErrorMessage ? Container()) 出现错误。它显示“条件必须具有 'bool' 的静态类型”。
    • 我有一个 ?,而 : 应该是。请重试。
    【解决方案2】:

    The solution that @thought7878 推荐建议将中断逻辑包装在WidgetsBinding.instance.addPostFrameCallback((_) { } 中。例如:

    builder: (context, AsyncSnapshot<Object> snapshot) {
      if (snapshot.hasError) {
        WidgetsBinding.instance.addPostFrameCallback((_) {
          Scaffold.of(context)
            ..hideCurrentSnackBar()
            ..showSnackBar(
              // SnackBar
            );
        });
        return Container();
      }
      // Rest ...
    }
    

    【讨论】:

      【解决方案3】:

      您可以这样尝试吗:修改 _showSnackBar 并传递上下文

      _showSnackBar(var text, BuildContext context) {
        ScaffoldKey.of(context)
        .showSnackBar(new SnackBar(content: new Text(text)));
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-17
        • 2020-11-28
        • 2020-10-26
        • 2013-01-26
        • 1970-01-01
        相关资源
        最近更新 更多