首页 > 解决方案 > 通过单击 FancyBottomNavigation 选项卡屏幕上的列表项导航到新屏幕时,Flutter 应用程序崩溃

问题描述

我正在重建一个已经使用 Flutter 使用原生 Android 开发的应用程序。我是 Flutter 和 Dart 的新手。但是,我保留在我以前的 UI 中,并进行了最小的更改。但是,到目前为止,浏览一切都做得很好。所以,让我解释一下我的工作。B'coz 我找不到这个问题的确切根本原因。首先,我有一个带抽屉的主屏幕。在其中,我使用 FancyBottomNavigation 实现了一个带有 3 个子选项卡屏幕的bottomNavigationBar. 一个选项卡有一个从 PHP 脚本中检索数据的动态列表视图。这部分完全可以正常工作,没有任何错误。接下来,我为每个项目列表实现了一个 onTap。当用户单击这些列表项时,导航到具有另一个动态列表视图的第三个屏幕。这就是错误的来源。第三个屏幕加载了列表数据而没有崩溃。但是这个导航抛出异常。此外,该屏幕上还有底部导航栏的剩余部分。然后,当按下后退按钮时应用程序崩溃。我已经多次尝试解决这个在 Internet 上解决的问题。* 为选项卡实现了 PageStorage * 为控制器添加了 dispose * MetiialApp 用于第三个屏幕,而不是 Container、Column...

所以,我需要解决这个崩溃问题并从第三个屏幕中删除那个底部导航栏图标。请帮助我解决这些问题并了解根本原因。

标签子屏幕 - 没关系

此屏幕 - 应删除此剩余部分

请参阅下面的导航到第三个屏幕时的例外情况。

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building TickerMode(mode: disabled):
setState() or markNeedsBuild() called during build.

This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: Overlay-[LabeledGlobalKey<OverlayState>#9de6b]
  state: OverlayState#b259c(entries: [OverlayEntry#ab600(opaque: true; maintainState: false), OverlayEntry#dfdfa(opaque: false; maintainState: true), OverlayEntry#69acb(opaque: true; maintainState: false), OverlayEntry#fcf4a(opaque: false; maintainState: true), OverlayEntry#2439e(opaque: false; maintainState: false)])
The widget which was currently being built when the offending call was made was: TickerMode
  mode: disabled
The relevant error-causing widget was: 
  MaterialApp file:///D:/DEVELOPMENT/Flutter/quiz_master/lib/screens/home_screen.dart:21:12
When the exception was thrown, this was the stack: 
#0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3896:11)
#1      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3911:6)
#2      State.setState (package:flutter/src/widgets/framework.dart:1168:14)
#3      _FancyBottomNavigationState._setSelected (package:fancy_bottom_navigation/fancy_bottom_navigation.dart:194:21)
#4      _FancyBottomNavigationState.didChangeDependencies (package:fancy_bottom_navigation/fancy_bottom_navigation.dart:64:5)
...
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ (2) Exception caught by widgets library ═══════════════════════════════════════════════════
'package:flutter/src/widgets/framework.dart': Failed assertion: line 5158 pos 14: 'oldChild == null || oldChild._debugLifecycleState == _ElementLifecycle.active': is not true.
The relevant error-causing widget was: 
  Scaffold file:///D:/DEVELOPMENT/Flutter/quiz_master/lib/screens/home_screen.dart:23:13
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ (3) Exception caught by widgets library ═══════════════════════════════════════════════════
'package:flutter/src/widgets/framework.dart': Failed assertion: line 3037 pos 12: '_debugLifecycleState == _ElementLifecycle.active
        && widget != null
        && newWidget != null
        && newWidget != widget
        && depth != null
        && _active
        && Widget.canUpdate(widget, newWidget)': is not true.
The relevant error-causing widget was: 
  FancyBottomNavigation file:///D:/DEVELOPMENT/Flutter/quiz_master/lib/screens/home_screen.dart:25:30
════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (10800): *JSON DATA*

请参阅下文了解按下返回按钮时的例外情况。(应用程序崩溃)

════════ (5) Exception caught by widgets library ═══════════════════════════════════════════════════
'package:flutter/src/widgets/framework.dart': Failed assertion: line 4277 pos 12: 'child == _child': is not true.
The relevant error-causing widget was: 
  MaterialApp file:///D:/DEVELOPMENT/Flutter/quiz_master/lib/screens/home_screen.dart:21:12
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ (6) Exception caught by widgets library ═══════════════════════════════════════════════════
Duplicate GlobalKey detected in widget tree.
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ (7) Exception caught by widgets library ═══════════════════════════════════════════════════
'package:flutter/src/widgets/framework.dart': Failed assertion: line 5540 pos 12: '_children.contains(child)': is not true.
The relevant error-causing widget was: 
  MaterialApp file:///D:/DEVELOPMENT/Flutter/quiz_master/lib/screens/home_screen.dart:21:12
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ (8) Exception caught by widgets library ═══════════════════════════════════════════════════
The getter 'userGestureInProgress' was called on null.
Receiver: null
Tried calling: userGestureInProgress
The relevant error-causing widget was: 
  MaterialApp file:///D:/DEVELOPMENT/Flutter/quiz_master/lib/screens/home_screen.dart:21:12
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ (9) Exception caught by widgets library ═══════════════════════════════════════════════════
Duplicate GlobalKey detected in widget tree.
════════════════════════════════════════════════════════════════════════════════════════════════════

HomeScreen - 带有 3 个选项卡的第一个屏幕

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

    class _MyHomePageState extends State<MyHomePage> {
  int currentPage = 0;
  final _tabOptions = [ HomeScreen(), LearnHomeScreen(), DiscussionHomeScreen() ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: _tabOptions[currentPage],
        bottomNavigationBar: FancyBottomNavigation(
          circleColor: HexColor("#3DAEDF"),
          inactiveIconColor: HexColor("#3DAEDF"),
          tabs: [
            TabData(iconData: Icons.home, title: "Home"),
            TabData(iconData: Icons.book, title: "Learn"),
            TabData(iconData: Icons.chat, title: "Discussion")
          ],
          onTabChangedListener: (position) {
            setState(() {
              currentPage = position;
            });
          },
        ),
      ),
    );
  }
}

子选项卡屏幕 - 包括列表视图的第二个屏幕

class LearnHomeScreen extends StatefulWidget {
  @override
  _LearnHomeScreenState createState() => _LearnHomeScreenState();
}

class _LearnHomeScreenState extends State<LearnHomeScreen> with TickerProviderStateMixin {

  AnimationController animationController;
  Animation<double> topBarAnimation;
  final ScrollController scrollController = ScrollController();
  double topBarOpacity = 0;
  Future<List<Subject>> subjectsList;
  List<Widget> listViews = <Widget>[];
  String uri;

  @override
  void initState() {
    uri = "xxxxx";
    subjectsList = getSubjects();
    animationController = AnimationController(duration: const Duration(milliseconds: 1000), vsync: this);

    topBarAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
        CurvedAnimation(
            parent: animationController,
            curve: Interval(0, 0.5, curve: Curves.fastOutSlowIn)));

    addAllListData();

    scrollController.addListener(() {
      if (scrollController.offset >= 24) {
        if (topBarOpacity != 1.0) {
          setState(() {
            topBarOpacity = 1.0;
          });
        }
      } else if (scrollController.offset <= 24 &&
          scrollController.offset >= 0) {
        if (topBarOpacity != scrollController.offset / 24) {
          setState(() {
            topBarOpacity = scrollController.offset / 24;
          });
        }
      } else if (scrollController.offset <= 0) {
        if (topBarOpacity != 0.0) {
          setState(() {
            topBarOpacity = 0.0;
          });
        }
      }
    });
    super.initState();
  }

  Future<bool> getData() async {
    await Future<dynamic>.delayed(const Duration(milliseconds: 200));
    return true;
  }

  Future<List<Subject>> getSubjects() async {
    var res = await http.get(uri);

    if (res.statusCode == 200) {
      print(res.body);
      List<dynamic> body = jsonDecode(res.body);
      List<Subject> subjectList = body
          .map(
            (dynamic item) => Subject.fromJson(item),
      ).toList();

      return subjectList;
    } else {
      throw "Can't get subjects.";
    }
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  void addAllListData() {
    listViews.add(
      getListView(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.grey[50],
      child: Scaffold(
        backgroundColor: Colors.transparent,
        body: Stack(
          children: <Widget>[
            getListView(),
            getAppBarUI(),
            SizedBox(
              height: MediaQuery.of(context).padding.bottom,
            )
          ],
        ),
      ),
    );
  }

  Widget getListView(){
    return Container(
      decoration: new BoxDecoration(
        image: DecorationImage(
          image: new ExactAssetImage('assets/images/main_background.png'),
          fit: BoxFit.cover,
        ),
      ),
      child: FutureBuilder(
        future: subjectsList,
        builder: (BuildContext context, AsyncSnapshot<List<Subject>> snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.waiting:
              return Center(child: Lottie.asset('assets/animations/dotloading.json'));
            case ConnectionState.done:
              if (snapshot.hasError) {
                return new Text('Error: ${snapshot.error}');
              }else {
                List<Subject> subjects = snapshot.data;
                return ListView.builder(
                  controller: scrollController,
                  itemCount: subjects.length,
                  padding: const EdgeInsets.only(top: 110, bottom: 25),
                  scrollDirection: Axis.vertical,
                  itemBuilder: (BuildContext context, int index) {
                    final int count = subjects.length > 10 ? 10 : subjects.length;
                    final Animation<double> animation =
                    Tween<double>(begin: 0.0, end: 1.0).animate(
                        CurvedAnimation(
                            parent: animationController,
                            curve: Interval(
                                (1 / count) * index, 1.0,
                                curve: Curves.fastOutSlowIn)));
                    animationController.forward();
                    return SubjectListView(
                      callback: () {},
                      subjectData: subjects[index],
                      animation: animation,
                      animationController: animationController,
                    );
                  },
                );
              }
              break;
            default:
              return Container();// also check your listWidget(snapshot) as it may return null.
          }
        },

      ),
    );
  }

  Widget getAppBarUI() {
    return Column(
      children: <Widget>[
        AnimatedBuilder(
          animation: animationController,
          builder: (BuildContext context, Widget child) {
            return FadeTransition(
              opacity: topBarAnimation,
              child: Transform(
                transform: Matrix4.translationValues(
                    0.0, 30 * (1.0 - topBarAnimation.value), 0.0),
                child: Container(
                  decoration: BoxDecoration(
                    color: Colors.white.withOpacity(topBarOpacity),
                    borderRadius: const BorderRadius.only(
                      bottomLeft: Radius.circular(32.0),
                    ),
                    boxShadow: <BoxShadow>[
                      BoxShadow(
                          color: Colors.grey
                              .withOpacity(0.4 * topBarOpacity),
                          offset: const Offset(1.1, 1.1),
                          blurRadius: 10.0),
                    ],
                  ),
                  child: Column(
                    children: <Widget>[
                      SizedBox(
                        height: MediaQuery.of(context).padding.top,
                      ),
                      Padding(
                        padding: EdgeInsets.only(
                            left: 16,
                            right: 16,
                            top: 2,
                            bottom: 8 - 8.0 * topBarOpacity),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            Expanded(
                              child: Padding(
                                padding: const EdgeInsets.only(left: 50, top: 15, bottom: 20),
                                child: Text(
                                  'Choose a subject!',
                                  textAlign: TextAlign.left,
                                  style: TextStyle(
                                    fontFamily: "Roboto",
                                    fontWeight: FontWeight.w600,
                                    fontSize: 26 + 6 - 6 * topBarOpacity,
                                    letterSpacing: 1,
                                    color: Colors.black,
                                  ),
                                ),
                              ),
                            ),
                          ],
                        ),
                      )
                    ],
                  ),
                ),
              ),
            );
          },
        ),
      ],
    );
  }
}

onTap - 列表视图项

child: GestureDetector(
            onTap: (){
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return PaperSelectScreen(
                  subjectName: subjectData.subject,
                  imageName: subjectData.image,
                );
              }));
            },

第三个屏幕 - 包括 seconf 动态列表视图

class PaperSelectScreen extends StatefulWidget {

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

  const PaperSelectScreen({Key key, this.subjectName, this.imageName}) : super(key: key);
  final subjectName;
  final imageName;
}

class _PaperSelectScreenState extends State<PaperSelectScreen> with TickerProviderStateMixin {

  static String subjectName;
  static String imageName;
  final ScrollController scrollController = ScrollController();
  AnimationController animationController;
  Animation<double> topBarAnimation;
  double topBarOpacity = 0;
  Future<List<Paper>> paperList;
  List<Widget> listViews = <Widget>[];
  String uri;

  @override
  void initState() {
    super.initState();
    subjectName = widget.subjectName;
    imageName = widget.imageName;
    uri = "xxxxxx";
    paperList = getPapers();
    animationController = AnimationController(duration: const Duration(milliseconds: 1000), vsync: this);

    topBarAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
        CurvedAnimation(
            parent: animationController,
            curve: Interval(0, 0.5, curve: Curves.fastOutSlowIn)));

    addAllListData();

    scrollController.addListener(() {
      if (scrollController.offset >= 24) {
        if (topBarOpacity != 1.0) {
          setState(() {
            topBarOpacity = 1.0;
          });
        }
      } else if (scrollController.offset <= 24 &&
          scrollController.offset >= 0) {
        if (topBarOpacity != scrollController.offset / 24) {
          setState(() {
            topBarOpacity = scrollController.offset / 24;
          });
        }
      } else if (scrollController.offset <= 0) {
        if (topBarOpacity != 0.0) {
          setState(() {
            topBarOpacity = 0.0;
          });
        }
      }
    });
  }

  Future<bool> getData() async {
    await Future<dynamic>.delayed(const Duration(milliseconds: 200));
    return true;
  }

  Future<List<Paper>> getPapers() async {
    var res = await http.get(uri);

    if (res.statusCode == 200) {
      print(res.body);
      List<dynamic> body = jsonDecode(res.body);
      List<Paper> paperList = body
          .map(
            (dynamic item) => Paper.fromJson(item),
      ).toList();
      return paperList;
    } else {
      throw "Can't get subjects.";
    }
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  void addAllListData() {
    listViews.add(
      getListView(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.grey[50],
      child: Scaffold(
        backgroundColor: Colors.transparent,
        body: Stack(
          children: <Widget>[
            getListView(),
            getAppBarUI(),
            /*Padding(
              padding: const EdgeInsets.only(left: 8,top: 30),
              child: IconButton(
                icon: Icon(Icons.arrow_back),
                color: Colors.black,
                onPressed: (){
                  Navigator.pop(context);
                },
              ),
            ),*/
            SizedBox(
              height: MediaQuery.of(context).padding.bottom,
            )
          ],
        ),
      ),
    );
  }

  Widget getListView(){
    return Container(
      decoration: new BoxDecoration(
        image: DecorationImage(
          image: new ExactAssetImage('assets/images/main_background.png'),
          fit: BoxFit.cover,
        ),
      ),
      child: FutureBuilder(
        future: paperList,
        builder: (BuildContext context, AsyncSnapshot<List<Paper>> snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.waiting:
              return Center(child: Lottie.asset('assets/animations/dotloading.json'));
            case ConnectionState.done:
              if (snapshot.hasError) {
                return new Text('Error: ${snapshot.error}');
              }else {
                List<Paper> paper = snapshot.data;
                return ListView.builder(
                  controller: scrollController,
                  itemCount: paper.length,
                  padding: const EdgeInsets.only(top: 110, bottom: 25),
                  scrollDirection: Axis.vertical,
                  itemBuilder: (BuildContext context, int index) {
                    final int count =
                    paper.length > 10 ? 10 : paper.length;
                    final Animation<double> animation =
                    Tween<double>(begin: 0.0, end: 1.0).animate(
                        CurvedAnimation(
                            parent: animationController,
                            curve: Interval(
                                (1 / count) * index, 1.0,
                                curve: Curves.fastOutSlowIn)));
                    animationController.forward();
                    return PaperListView(
                      callback: () {},
                      paperData: paper[index],
                      image: imageName,
                      animation: animation,
                      animationController: animationController,
                    );
                  },
                );
              }
              break;
            default:
              return Container();// also check your listWidget(snapshot) as it may return null.
          }
        },

      ),
    );
  }

  Widget getAppBarUI() {
    return Column(
      children: <Widget>[
        AnimatedBuilder(
          animation: animationController,
          builder: (BuildContext context, Widget child) {
            return FadeTransition(
              opacity: topBarAnimation,
              child: Transform(
                transform: Matrix4.translationValues(
                    0.0, 30 * (1.0 - topBarAnimation.value), 0.0),
                child: Container(
                  decoration: BoxDecoration(
                    color: Colors.white.withOpacity(topBarOpacity),
                    borderRadius: const BorderRadius.only(
                      bottomLeft: Radius.circular(32.0),
                    ),
                    boxShadow: <BoxShadow>[
                      BoxShadow(
                          color: Colors.grey
                              .withOpacity(0.4 * topBarOpacity),
                          offset: const Offset(1.1, 1.1),
                          blurRadius: 10.0),
                    ],
                  ),
                  child: Column(
                    children: <Widget>[
                      SizedBox(
                        height: MediaQuery.of(context).padding.top,
                      ),
                      Padding(
                        padding: EdgeInsets.only(
                            left: 16,
                            right: 16,
                            top: 2,
                            bottom: 8 - 8.0 * topBarOpacity),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            Expanded(
                              child: Padding(
                                padding: const EdgeInsets.only(left: 50, top: 15, bottom: 20),
                                child: Text(
                                  'Choose a paper!',
                                  textAlign: TextAlign.left,
                                  style: TextStyle(
                                    fontFamily: "Roboto",
                                    fontWeight: FontWeight.w600,
                                    fontSize: 26 + 6 - 6 * topBarOpacity,
                                    letterSpacing: 1,
                                    color: Colors.black,
                                  ),
                                ),
                              ),
                            ),
                          ],
                        ),
                      )
                    ],
                  ),
                ),
              ),
            );
          },
        ),
      ],
    );
  }
}

标签: flutterflutter-layoutflutter-navigationflutter-stateflutter-bottomnavigation

解决方案


此错误“在构建期间调用了 setState() 或 markNeedsBuild()。” 甚至在Widget build完成渲染小部件之前调用 setState() 引起的。

您可以在这里做的一种解决方法是等待小部件被渲染

WidgetsBinding.instance.addPostFrameCallback((_){
  // Add your code here
});

或使用 SchedulerBinding

SchedulerBinding.instance.addPostFrameCallback((_) {
  // Add your code here
});

推荐阅读