Search code examples
fluttersupabasesnackbar

Exception has occurred. (dependOnInheritedWidgetOfExactType<_ScaffoldMessengerScope>()was called before _AccountPageState.initState() completed


**Every time I open a specific page on my App this error shows up: **

Exception has occurred. FlutterError (dependOnInheritedWidgetOfExactType<_ScaffoldMessengerScope>() or dependOnInheritedElement() was called before _AccountPageState.initState() completed. When an inherited widget changes, for example if the value of Theme.of() changes, its dependent widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor or an initState() method, then the rebuilt dependent widget will not reflect the changes in the inherited widget. Typically references to inherited widgets should occur in widget build() methods. Alternatively, initialization based on inherited widgets can be placed in the didChangeDependencies method, which is called after initState and whenever the dependencies change thereafter.)

This is my Code on the Page where the error comes:

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:thrive/components/avatar.dart';
import 'package:thrive/main.dart';
import 'package:thrive/pages/creator_page.dart';
import 'package:thrive/pages/userlogin.dart';

class AccountPage extends StatefulWidget {
  const AccountPage({super.key});

  @override
  State<AccountPage> createState() => _AccountPageState();
}

class _AccountPageState extends State<AccountPage> {
  final _usernameController = TextEditingController();
  final _websiteController = TextEditingController();
  String? _imageUrl;

  String? _avatarUrl;
  var _loading = true;

  /// Called once a user id is received within `onAuthenticated()`
  Future<void> _getProfile() async {
    setState(() {
      _loading = true;
    });

    try {
      final userId = supabase.auth.currentSession!.user.id;
      final data =
          await supabase.from('profiles').select().eq('id', userId).single();
      _usernameController.text = (data['username'] ?? '') as String;
      _websiteController.text = (data['website'] ?? '') as String;
      _imageUrl = data['avatar_url'];
      _avatarUrl = (data['avatar_url'] ?? '') as String;
    } on PostgrestException catch (error) {
      if (mounted) context.showSnackBar(error.message, isError: true);
    } catch (error) {
      if (mounted) {
        context.showSnackBar('Unexpected error occurred', isError: true);
      }
    } finally {
      if (mounted) {
        setState(() {
          _loading = false;
        });
      }
    }
  }

  /// Called when user taps `Update` button
  Future<void> _updateProfile() async {
    setState(() {
      _loading = true;
    });
    final userName = _usernameController.text.trim();
    final website = _websiteController.text.trim();
    final user = supabase.auth.currentUser;
    final updates = {
      'id': user!.id,
      'username': userName,
      'website': website,
      'updated_at': DateTime.now().toIso8601String(),
    };
    try {
      await supabase.from('profiles').upsert(updates);
      if (mounted) context.showSnackBar('Successfully updated profile!');
    } on PostgrestException catch (error) {
      if (mounted) context.showSnackBar(error.message, isError: true);
    } catch (error) {
      if (mounted) {
        context.showSnackBar('Unexpected error occurred', isError: true);
      }
    } finally {
      if (mounted) {
        setState(() {
          _loading = false;
        });
      }
    }
  }

  Future<void> _signOut() async {
    try {
      await supabase.auth.signOut();
    } on AuthException catch (error) {
      if (mounted) context.showSnackBar(error.message, isError: true);
    } catch (error) {
      if (mounted) {
        context.showSnackBar('Unexpected error occurred', isError: true);
      }
    } finally {
      if (mounted) {
        Navigator.of(context).pushReplacement(
          MaterialPageRoute(builder: (_) => const Userlogin()),
        );
      }
    }
  }

  @override
  void initState() {
    super.initState();
    _getProfile();
  }

  @override
  void dispose() {
    _usernameController.dispose();
    _websiteController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Profile'), actions: <Widget>[
          IconButton(
            icon: const Icon(Icons.group_add_rounded),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) => Creatorpage(),),);
            },
          ),
        ],
       ),
      body: ListView(
        padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12),
        children: [
          Avatar(imageUrl: _imageUrl, onUpload: (imageUrl) async {
            setState(() {
              _imageUrl = imageUrl;
            });
            final userId = supabase.auth.currentUser!.id;
            await supabase.from('profiles').update({'avatar_url': imageUrl}).eq('id', userId);
          }),

          SizedBox(height: 12),

          TextFormField(
            controller: _usernameController,
            decoration: InputDecoration(labelText: 'User Name', border: OutlineInputBorder(borderRadius: BorderRadius.circular(12))),
          ),
          const SizedBox(height: 18),
          TextFormField(
            controller: _websiteController,
            decoration: InputDecoration(labelText: 'Headline', border: OutlineInputBorder(borderRadius: BorderRadius.circular(12))),
          ),
          const SizedBox(height: 18),
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            ),
            onPressed: _loading ? null : _updateProfile,
            child: Text(_loading ? 'Saving...' : 'Update', style: TextStyle(color: Theme.of(context).colorScheme.surface),),
          ),
          const SizedBox(height: 18),
          TextButton(onPressed: _signOut, child: Text('Sign Out', style: TextStyle(color: Theme.of(context).colorScheme.inversePrimary),)),
        ],
      ),
    );
  }
}

And here is my main.dart:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_nav_bar/google_nav_bar.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:thrive/components/dark_mode.dart';
import 'package:thrive/components/light_mode.dart';
import 'package:thrive/pages/account_page.dart';
import 'package:thrive/pages/homepage.dart';

Future<void> main() async {
  await Supabase.initialize(
    url: '_________________',
    anonKey: '_____________',
  );
  runApp(const MyApp());
}

final supabase = Supabase.instance.client;

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  int _selectedIndex = 0;

  // Navigation list for the navbar
  static const List<Widget> _widgetOptions = <Widget>[
    Homepage(),
    Text("Communities"),
    AccountPage(),
  ];
  
  @override
  Widget build(BuildContext context) {
        SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      systemNavigationBarColor: Colors.transparent,
    ));
    return MaterialApp(title: 'Supabase Flutter',
    debugShowCheckedModeBanner: false,
    home: Scaffold(

      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),

      // NavBar
          bottomNavigationBar: Padding(

            padding: const EdgeInsets.symmetric(
              horizontal: 15.0, 
              vertical: 20.0
              ),
            child: GNav(
              
              gap: 8,
              padding: EdgeInsets.all(16),
              activeColor: Theme.of(context).colorScheme.surface,
              tabBackgroundColor: Theme.of(context).colorScheme.secondary,
              tabs: [

                // HomePage
                GButton(
                  icon: Icons.home_rounded, 
                  iconColor: Theme.of(context).colorScheme.secondary,
                  text: 'Home',
                  ),

                  // CommunitiesPage
                GButton(
                  icon: Icons.people_alt_rounded, 
                  iconColor: Theme.of(context).colorScheme.secondary,
                  text: 'Communities',
                  ),

                  // ProfilePage
                GButton(
                  icon: Icons.person_rounded, 
                  iconColor: Theme.of(context).colorScheme.secondary,
                  text: 'Profile',
                  ),
              ],
              selectedIndex: _selectedIndex,
              onTabChange: (index){
                setState(() {
                  _selectedIndex = index;
                });
              },
            ),
          ),
    ),
    theme: lightMode,
    darkTheme: darkMode,
    );
  }
}

extension ContextExtension on BuildContext {
  void showSnackBar(String message, {bool isError = false}) {
    ScaffoldMessenger.of(this).showSnackBar(
      SnackBar(
        content: Text(message),
        backgroundColor: isError
            ? Theme.of(this).colorScheme.error
            : Theme.of(this).snackBarTheme.backgroundColor,
      ),
    );
  }
}

The error comes since I added this Page:

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:thrive/components/avatar_display.dart';
import 'package:thrive/main.dart';

class Creatorpage extends StatefulWidget {
  const Creatorpage({super.key});

  @override
  State<Creatorpage> createState() => _CreatorpageState();
}

  class _CreatorpageState extends State<Creatorpage> {
  final _usernameController = TextEditingController();
  final _websiteController = TextEditingController();
  String? _imageUrl;

  String? _avatarUrl;
  var _loading = true;

  /// Called once a user id is received within `onAuthenticated()`
  Future<void> _getProfile() async {
    setState(() {
      _loading = true;
    });

    try {
      final userId = supabase.auth.currentSession!.user.id;
      final data =
          await supabase.from('profiles').select().eq('id', userId).single();
      _imageUrl = data['avatar_url'];
      _avatarUrl = (data['avatar_url'] ?? '') as String;
    } on PostgrestException catch (error) {
      if (mounted) context.showSnackBar(error.message, isError: true);
    } catch (error) {
      if (mounted) {
        context.showSnackBar('Unexpected error occurred', isError: true);
      }
    } finally {
      if (mounted) {
        setState(() {
          _loading = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(

      // AppBar
      appBar: AppBar(
        leading: IconButton(
          onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) => Creatorpage(),),);
          }, 
          icon: Icon(Icons.arrow_back_ios_new_rounded),
          ),
         ),

         body: ListView(
        padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12),
        children: [
          AvatarDisplay(imageUrl: _imageUrl, onUpload: (imageUrl) async {
            setState(() {
              _imageUrl = imageUrl;
            });
            final userId = supabase.auth.currentUser!.id;
            await supabase.from('profiles').update({'avatar_url': imageUrl}).eq('id', userId);
          }),
        ]
      )
    );
  }
 }

Before I added this new Page a Account page comes up. And now the app crashes.

Can someone help me?


Solution

  • Because you're trying to access the ScaffoldMessenger in the initState method of your _AccountPageState class. The initState method is called before the widget is built, so the ScaffoldMessenger is not available yet.

    You can move the call to _getProfile to the didChangeDependencies method, which is called after the widget is built and the dependencies are established.