3

Please check out this 36 seconds video for more clarity, cause it was getting too verbose explaning things : https://www.youtube.com/watch?v=W6WdQuLjrCs

My best guess

  • It's due to the provider.

App structure ->

Outer Page -> NoteList Page

The Outer Page code :


class OuterPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return OuterPageState();
  }
}

class OuterPageState extends State<OuterPage> {
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  int _selectedTab = 0;
  var noteList;
  final _pageOptions = [
    NoteList(),
    AnotherPageScreen(),
  ];

  @override
  Widget build(BuildContext context) {
    var noteProvider = Provider.of<NotesProvider>(context, listen: false);
    var customFabButton;
    if (_selectedTab == 0) {
      customFabButton = FloatingActionButton(
        // Password section
        onPressed: () {
          navigateToDetail(context, Note('', '', 2), 'Add Note');
        },
        child: Icon(Icons.add),
      );
~~~ SNIP ~~~

The Notes Tab aka NoteList page code :

class NoteList extends StatefulWidget {
  NoteList();

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

class NoteListState extends State<NoteList> {
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  List<Note> noteList;
  int count = 0;

  @override
  Widget build(BuildContext context) {
    Provider.of<NotesProvider>(context).getNotes();
    return Scaffold(
        key: _scaffoldKey,
        body: Provider.of<NotesProvider>(context).count > 0
            ? NoteListScreen(_scaffoldKey)
            : CircularProgressIndicator());
  }
}

For full code : check here : https://github.com/LuD1161/notes_app/tree/reusable_components

Update 1 - Possible solution is FutureBuilder

I know that there's a possible solution with FutureBuilder but I think even Provider is apt for this use case.

Moreover is it an anti-pattern here ?

Also, please don't suggest another package for the same thing, if possible try limiting the solution to Provider or base libraries.

Update 2 - Not possible with FutureBuilder

FutureBuilder can't be used here because there's a delete button in the list tile and hence when the note gets deleted the note list won't get updated.

Aseem Shrey
  • 121
  • 4

1 Answers1

0

The issue is coming because of the getNotes function you are calling from build method. You are calling notifyListeners from that function. It again re-builds the widget and calls the build method again and this cycle continues.

You either need to set false to the listen property of provider, but it will break your functionality. To fix this, you have to move the getNotes call from build function to initState like following:

@override
void initState() {
  super.initState();
  postInit(() {
    Provider.of<NotesProvider>(context).getNotes();
  });
}

Implement postInit (Reference: Flutter Issue 29515):

extension StateExtension<T extends StatefulWidget> on State<T> {
  Stream waitForStateLoading() async* {
    while (!mounted) {
      yield false;
    }
    yield true;
  }

  Future<void> postInit(VoidCallback action) async {
    await for (var isLoaded in waitForStateLoading()) {}
    action();
  }
}

Note: Instead of writing the postInit code, you can also use after_init package for same purpose.

Several other posts discussing similar kind of issues:

Midhun MP
  • 103,496
  • 31
  • 153
  • 200
  • thanks fo the prompt reply. But isn't it too much for just a simple thing as fetching notes. I could've easily fetched it using future builder but is it antipattern to fetch using provider ? – Aseem Shrey Apr 28 '20 at 17:58
  • @AseemShrey: 1) You can pass the list from it's parent class (you are already listening for changes there) 2) I can't say exactly whether using future builder is an anti-pattern, I didn't see anybody saying that and I believe that we can mix&match different state management techniques properly – Midhun MP Apr 28 '20 at 18:09
  • @AseemShrey Also check the posts in the links included with the updated answer – Midhun MP Apr 28 '20 at 18:13
  • Yes, I did take a look at it. By anti-pattern, I meant using provider to fetch data, being an antipattern – Aseem Shrey Apr 28 '20 at 18:15
  • @AseemShrey Yes, I think that implementation has issues. If you are interested check a similar implementation of a todo list, which is done in a nice way: https://dev.to/shakib609/create-a-todos-app-with-flutter-and-provider-jdh – Midhun MP Apr 28 '20 at 18:21
  • Thanks a lot, really this helps a lot. Actually I was looking at some code examples, myself and that led me to implement this way. This was the code I looked at : https://github.com/alfredasare/flutter-todo/blob/master/lib/screens/home_screen.dart#L28 . Here you can see the person calling `getTaskList` in the `Build` function. Both the codes are almost similar, however the one you mentioned doesn't look to be doing this. Checking that out. The implementation you sent doesn't use `db` and hence doesn't need any call like `getTaskList`. Probably that's the catch there. – Aseem Shrey Apr 28 '20 at 18:29
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/212722/discussion-between-aseem-shrey-and-midhun-mp). – Aseem Shrey Apr 28 '20 at 18:30