1

I am rebuilding an application that has already been developed with native Android using Flutter. I'm new to Flutter and Dart. But, I staying in my previous UI with minimal changes. However, so far everything has been well done with browsing. So, Let me explain a little bit about what I do. B'coz I can't find the exact root cause of this issue. First of all, I have a Home screen with Drawer. Inside that, I've implemented a bottomNavigationBar with 3 child tab screens using FancyBottomNavigation. One tab has a dynamic listview that retrieves data from PHP scripts. This part totally work fine without any error. Next, I've implemented an onTap for each list of items. When the user clicks these list items, navigate to the third screen, which has another dynamic list view. This is where the error comes from. The third screen loaded with the list data without the crash. But the Exceptions throwing with this navigation. Also, there is a remaining part of bottomNavigationBar on this screen. Then, the app crashes when the back button is pressed. I've already been made several attempts to resolve this issue that address on the Internet. * Implemented the PageStorage for tabs * Add the dispose for controllers * MetirialApp for the third screen instead the Container, Column...

So, I need to fix this crashing issue and remove that bottomNavigationBar icon from the third screen. Please kindly help me to resolve these issues and understand the root cause.

tab child screen - this is okay

thisr screen - this remaining part should be removed

Please refer below for the exceptions when navigating to the third screen.

════════ 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*

Please refer below for the exceptions when pressing the back button. (App crashed)

════════ (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 - first screen with 3 tabs

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

Child Tab Screen - Second Screen which includes list view

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 - Listview Item

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

Third Screen - which include seconf dynamic listview

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,
                                  ),
                                ),
                              ),
                            ),
                          ],
                        ),
                      )
                    ],
                  ),
                ),
              ),
            );
          },
        ),
      ],
    );
  }
}
SUDESH KUMARA
  • 891
  • 9
  • 18

1 Answers1

0

This error "setState() or markNeedsBuild() called during build." was caused by calling setState() even before Widget build finished rendering the widgets.

What you can do here as a workaround is to wait for the widgets to be rendered with

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

or by using SchedulerBinding

SchedulerBinding.instance.addPostFrameCallback((_) {
  // Add your code here
});
Omatt
  • 8,564
  • 2
  • 42
  • 144