当我尝试在构建器中显示SnackBar时出现错误

时间:2018-10-17 07:24:55

标签: flutter flutter-animation

这是我的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是一项从数据库获取数据的功能。

3 个答案:

答案 0 :(得分:2)

FutureBuilderStreamBuilder仅应用于构建窗口小部件,而不用于执行导航之类的逻辑。改用initState之类的

@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())
      ),
    ),
  );
}

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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 ...
}