Search code examples
flutterflutter-navigation

Flutter BuildContext navigation


I want to use the Navigator, but I get the error: Navigator operation requested with a context that does not include a Navigator, when I click on the floatingButton, I decided to make the same button for each note presented in the list, because the buttons that are already there work fine, the floattingButton for adding an note also works fine there. As I understand it, there is something wrong with the BuildContext, perhaps due to Future and async/await.

main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'add.dart';
import 'firebase_options.dart';
import 'note.dart';
import 'package:intl/intl.dart'; 
import 'edit.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  runApp(MainApp());
}

class MainApp extends StatefulWidget {
  @override
  _MainAppState createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {

  List<Note> listNotes = [
    Note(title: '1', content: 'abracadabra', lastEdited: DateTime.parse('2024-08-05')),
    Note(title: '2', content: 'abracadabra', lastEdited: DateTime.parse('2024-08-06')),
    Note(title: '3', content: 'abracadabra', lastEdited: DateTime.parse('2024-08-07')),
  ];

  final FirebaseManager firebaseManager = FirebaseManager();
  late Future<List<Note>> _notesFuture;


  void _fetchNotes() {
    setState(() {
      _notesFuture = firebaseManager.getNotes();
    });
  }

  @override
  void initState() {
    super.initState();
    firebaseManager.addNotes(listNotes);
    _fetchNotes();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Notes'),
        ),
        body: FutureBuilder<List<Note>>(
          future: _notesFuture,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return Center(child: CircularProgressIndicator());
            } else if (snapshot.hasError) {
              return Center(child: Text('Error: ${snapshot.error}'));
            } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
              return Center(child: Text('No notes available'));
            } else {
              List<Note> notes = snapshot.data!;
              return ListView.builder(
                itemCount: notes.length,
                itemBuilder: (context, index) {
                  Note note = notes[index];

                  return ListTile(
                    title: Text(note.title),
                    subtitle: Text(note.content),
                    trailing: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        IconButton(
                          icon: Icon(Icons.edit),
                          onPressed: () {
                            Navigator.push(
                              context,
                              MaterialPageRoute(
                                builder: (context) => EditNoteScreen(note: note,firebaseManager: firebaseManager,),
                              ),
                            ).then((value) => {
                              if (value == true){
                                _fetchNotes()
                              }
                            });
                          },
                        ),
                        IconButton(
                          icon: Icon(Icons.delete),
                          onPressed: () async {
                            await firebaseManager.deleteNoteByFieldId(note.id);
                            _fetchNotes();
                          },
                        ),
                        IconButton(
                          icon: Icon(Icons.add),
                          onPressed: () {
                            Navigator.push(
                              context,
                              MaterialPageRoute(
                                builder: (context) => AddNoteScreen(firebaseManager: firebaseManager),
                              ),
                            ).then((value) => {
                              if (value == true){
                                _fetchNotes()
                              }
                            });
                          },
                          )
                      ],
                    ),
                  );
                },
              );
            }
          },
        ),
        floatingActionButton: FloatingActionButton(
        onPressed: () {
          
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => AddNoteScreen(firebaseManager: firebaseManager),
            ),
          ).then((value) => {
            if (value == true){
              _fetchNotes()
            }
          });
        },
        child: Icon(Icons.add),
      ),
      ),
    );
  }
}

Solution

  • When you are creating your Scaffold you are creating it in the same context you use to create MaterialApp. The Navigator is provided by MaterialApp so it doesn't exist yet in the parent context. You need a child context in order to find the Navigator.

    Wrap your Scaffold in a Builder to get ahold of the child context, which will be provided to the builder when it is built.

    Like this:

     @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Builder(
            builder: (BuildContext context) => Scaffold(
              // Scaffold configuration as you have
            ),
          ),
         );
      }
    
    

    This is also why the navigation works in the widgets you build within FutureBuilder's provided context - FutureBuilder gets build in the parent context, but its build receives a child context where Navigator can be found.