Flutter Provider- Setting Streamprovider error in home.dart - flutter

I'm using a custom route to create a transition effect but I'm having some problems with a
value using Provider.
Error
Another exception was thrown: Error: Could not find the correct Provider<List<FireFavorites>> above this Favorites Widget
home.dart
return MultiProvider(
providers: [
StreamProvider.value(
value: db.streamFavorites(user.uid),
)
],
child: Scaffold(
body: StreamBuilder<NavBarItem>(
stream: _bottomNavBarBloc.itemStream,
initialData: _bottomNavBarBloc.defaultItem,
builder: (BuildContext context, AsyncSnapshot<NavBarItem> snapshot) {
switch (snapshot.data) {
case NavBarItem.SEARCH:
return Search();
case NavBarItem.BROWSE:
return Browse();
case NavBarItem.ICON:
return About();
case NavBarItem.FAVORITES:
return Favorites();
case NavBarItem.SETTINGS:
return Settings();
default:
return null;
}
},
),
bottomNavigationBar: ConvexBottomNav(navBarHandler: _navBarHandler),
),
);
}
custom route class
class MyCustomPageRoute extends MaterialPageRoute {
final Widget previousPage;
MyCustomPageRoute(
{this.previousPage, WidgetBuilder builder, RouteSettings settings})
: super(builder: builder, settings: settings);
#override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget currentPage) {
Animation<Offset> _slideAnimationPage1 =
Tween<Offset>(begin: Offset(0.0, 0.0), end: Offset(-1.0, 0.0))
.animate(animation);
Animation<Offset> _slideAnimationPage2 =
Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0))
.animate(animation);
return Stack(
children: <Widget>[
SlideTransition(position: _slideAnimationPage1, child: previousPage),
SlideTransition(position: _slideAnimationPage2, child: currentPage),
],
);
}
}
using the class like this which is in Browse(). I simply want to navigate to a document. But using MyCustomPageRoute causes the Provider error.
Navigator.push(
context,
MyCustomPageRoute(
previousPage: this,
builder: (context) => Document(
documentID: docid,
),
),
);
I would like to have the favorites StreamProvider in main.dart, but I don't have access to the authenticated user's uid there, or I don't know how to access it after it becomes available. Below is how I have Provider set up in main.dart. I can just make a simple request in home.dart, get the favorites and add them to Content() and the access them throughout the app, but I would like to have a stream.
main.dart
return MultiProvider(
providers: [
StreamProvider<FirebaseUser>.value(
value: FirebaseAuth.instance.onAuthStateChanged,
),
ChangeNotifierProvider<Content>.value(
value: Content(),
),
],
child: MaterialApp(
title: '',
theme: ThemeData(
fontFamily: 'OpenSans',
brightness: Brightness.light,
primaryColor: Color(0xff2398C3),
),
home: WelcomePage(),
),
);
Any idea how can this be fixed?

Related

A RouteState was used after being disposed Error

Let me explain my Flutter structure first. I have a flutter main application and another application added as a package that has a different routing method and navigation. app behavior is when I click on a card on the main app it will get me to the package app, but when I go back to the home interface which is the main app. I'm getting the following error.
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown while finalizing the widget tree:
A RouteState was used after being disposed.
What I have tried so far
I have tried to observe the navigation stack using route_observer_mixin but, it didn't work because I have two different navigations in the main app and the package.
if I try to remove the RouteState.dispose() in the package the error is gone, but that is a bad practice, right? because the memory leak could happen.
I'll put the related code section below for your reference.
Code section from main project main.dart file
class GalleryApp extends StatefulWidget {
// GalleryApp({super.key});
GalleryApp({
super.key,
this.initialRoute,
this.isTestMode = false,
});
late final String? initialRoute;
late final bool isTestMode;
final _auth = CampusAppsPortalAuth();
#override
State<GalleryApp> createState() => _GalleryAppState();
}
RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
class _GalleryAppState extends State<GalleryApp> {
late final String loginRoute = '/signin';
get isTestMode => false;
#override
Widget build(BuildContext context) {
return ModelBinding(
initialModel: GalleryOptions(
themeMode: ThemeMode.system,
textScaleFactor: systemTextScaleFactorOption,
customTextDirection: CustomTextDirection.localeBased,
locale: null,
timeDilation: timeDilation,
platform: defaultTargetPlatform,
isTestMode: isTestMode,
),
child: Builder(
builder: (context) {
final options = GalleryOptions.of(context);
return MaterialApp(
restorationScopeId: 'rootGallery',
title: 'Flutter Gallery',
debugShowCheckedModeBanner: false,
navigatorObservers: [routeObserver],
themeMode: options.themeMode,
theme: GalleryThemeData.lightThemeData.copyWith(
platform: options.platform,
),
darkTheme: GalleryThemeData.darkThemeData.copyWith(
platform: options.platform,
),
localizationsDelegates: const [
...GalleryLocalizations.localizationsDelegates,
LocaleNamesLocalizationsDelegate()
],
initialRoute: loginRoute,
supportedLocales: GalleryLocalizations.supportedLocales,
locale: options.locale,
localeListResolutionCallback: (locales, supportedLocales) {
deviceLocale = locales?.first;
return basicLocaleListResolution(locales, supportedLocales);
},
onGenerateRoute: (settings) {
return RouteConfiguration.onGenerateRoute(settings);
},
onUnknownRoute: (RouteSettings settings) {
return MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) =>
Scaffold(body: Center(child: Text('Not Found'))),
);
});
},
),
);
}
}
class RootPage extends StatelessWidget {
const RootPage({
super.key,
});
#override
Widget build(BuildContext context) {
return const ApplyTextOptions(
child: SplashPage(
child: Backdrop(),
),
);
}
}
Code section from main project Backdrop
class Backdrop extends StatefulWidget {
const Backdrop({super.key, this.settingsPage, this.homePage, this.loginPage});
final Widget? settingsPage;
final Widget? homePage;
final Widget? loginPage;
#override
State<Backdrop> createState() => _BackdropState();
}
RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
class _BackdropState extends State<Backdrop>
with TickerProviderStateMixin, RouteAware {
#override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute<dynamic>);
}
late AnimationController _settingsPanelController;
late AnimationController _iconController;
late FocusNode _settingsPageFocusNode;
late ValueNotifier<bool> _isSettingsOpenNotifier;
late Widget _settingsPage;
late Widget _homePage;
late Widget _unknownPage;
#override
void initState() {
super.initState();
_settingsPanelController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
_iconController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_settingsPageFocusNode = FocusNode();
_isSettingsOpenNotifier = ValueNotifier(false);
_settingsPage = widget.settingsPage ??
SettingsPage(
animationController: _settingsPanelController,
);
_homePage = widget.homePage ?? const HomePage();
_unknownPage = widget.homePage ?? const HomePage();
}
#override
void dispose() {
_settingsPanelController.dispose();
_iconController.dispose();
_settingsPageFocusNode.dispose();
_isSettingsOpenNotifier.dispose();
routeObserver.unsubscribe(this);
super.dispose();
}
#override
void didPush() {
final route = ModalRoute.of(context)!.settings.name;
print('didPush route: $route');
}
#override
void didPopNext() {
final route = ModalRoute.of(context)!.settings.name;
print('didPopNext route: $route');
}
#override
void didPushNext() {
final route = ModalRoute.of(context)!.settings.name;
print('didPushNext route: $route');
}
#override
void didPop() {
final route = ModalRoute.of(context)!.settings.name;
print('didPop route: $route');
}
void _toggleSettings() {
// Animate the settings panel to open or close.
if (_isSettingsOpenNotifier.value) {
_settingsPanelController.reverse();
_iconController.reverse();
} else {
_settingsPanelController.forward();
_iconController.forward();
}
_isSettingsOpenNotifier.value = !_isSettingsOpenNotifier.value;
}
Animation<RelativeRect> _slideDownSettingsPageAnimation(
BoxConstraints constraints) {
return RelativeRectTween(
begin: RelativeRect.fromLTRB(0, -constraints.maxHeight, 0, 0),
end: const RelativeRect.fromLTRB(0, 0, 0, 0),
).animate(
CurvedAnimation(
parent: _settingsPanelController,
curve: const Interval(
0.0,
0.4,
curve: Curves.ease,
),
),
);
}
Animation<RelativeRect> _slideDownHomePageAnimation(
BoxConstraints constraints) {
return RelativeRectTween(
begin: const RelativeRect.fromLTRB(0, 0, 0, 0),
end: RelativeRect.fromLTRB(
0,
constraints.biggest.height - galleryHeaderHeight,
0,
-galleryHeaderHeight,
),
).animate(
CurvedAnimation(
parent: _settingsPanelController,
curve: const Interval(
0.0,
0.4,
curve: Curves.ease,
),
),
);
}
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
final isDesktop = isDisplayDesktop(context);
bool signedIn = campusAppsPortalInstance.getSignedIn();
log('signedIn: $signedIn! ');
print('signedIn: $signedIn!');
log('is decktop $isDesktop');
final Widget settingsPage = ValueListenableBuilder<bool>(
valueListenable: _isSettingsOpenNotifier,
builder: (context, isSettingsOpen, child) {
return ExcludeSemantics(
excluding: !isSettingsOpen,
child: isSettingsOpen
? RawKeyboardListener(
includeSemantics: false,
focusNode: _settingsPageFocusNode,
onKey: (event) {
if (event.logicalKey == LogicalKeyboardKey.escape) {
_toggleSettings();
}
},
child: FocusScope(child: _settingsPage),
)
: ExcludeFocus(child: _settingsPage),
);
},
);
final Widget homePage = ValueListenableBuilder<bool>(
valueListenable: _isSettingsOpenNotifier,
builder: (context, isSettingsOpen, child) {
return ExcludeSemantics(
excluding: isSettingsOpen,
child: FocusTraversalGroup(child: _homePage),
);
},
);
final Widget unknownPage = ValueListenableBuilder<bool>(
valueListenable: _isSettingsOpenNotifier,
builder: (context, isSettingsOpen, child) {
return ExcludeSemantics(
excluding: isSettingsOpen,
child: FocusTraversalGroup(child: _unknownPage),
);
},
);
final Widget loginPage = ValueListenableBuilder<bool>(
valueListenable: _isSettingsOpenNotifier,
builder: (context, isSettingsOpen, child) {
return ExcludeSemantics(
excluding: isSettingsOpen,
child: FocusTraversalGroup(
child: LoginPage(
// onSignIn: (credentials) async {
// var signedIn = await authState.signIn(
// credentials.username, credentials.password);
// if (signedIn) {
// await routeState.go('/gallery');
// }
// },
),
),
);
},
);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: GalleryOptions.of(context).resolvedSystemUiOverlayStyle(),
child: Stack(
children: [
if (!isDesktop) ...[
// Slides the settings page up and down from the top of the
// screen.
PositionedTransition(
rect: _slideDownSettingsPageAnimation(constraints),
child: settingsPage,
),
// Slides the home page up and down below the bottom of the
// screen.
PositionedTransition(
rect: _slideDownHomePageAnimation(constraints),
child: homePage,
),
PositionedTransition(
rect: _slideDownHomePageAnimation(constraints),
child: loginPage,
),
],
if (isDesktop && signedIn) ...[
Semantics(sortKey: const OrdinalSortKey(2), child: homePage),
ValueListenableBuilder<bool>(
valueListenable: _isSettingsOpenNotifier,
builder: (context, isSettingsOpen, child) {
if (isSettingsOpen) {
return ExcludeSemantics(
child: Listener(
onPointerDown: (_) => _toggleSettings(),
child: const ModalBarrier(dismissible: false),
),
);
} else {
return Container();
}
},
),
Semantics(
sortKey: const OrdinalSortKey(3),
child: ScaleTransition(
alignment: Directionality.of(context) == TextDirection.ltr
? Alignment.topRight
: Alignment.topLeft,
scale: CurvedAnimation(
parent: _settingsPanelController,
curve: Curves.easeIn,
reverseCurve: Curves.easeOut,
),
child: Align(
alignment: AlignmentDirectional.topEnd,
child: Material(
elevation: 7,
clipBehavior: Clip.antiAlias,
borderRadius: BorderRadius.circular(40),
color: Theme.of(context).colorScheme.secondaryContainer,
child: Container(
constraints: const BoxConstraints(
maxHeight: 560,
maxWidth: desktopSettingsWidth,
minWidth: desktopSettingsWidth,
),
child: settingsPage,
),
),
),
),
),
],
if (isDesktop && !signedIn) ...[
Semantics(sortKey: const OrdinalSortKey(2), child: loginPage),
ValueListenableBuilder<bool>(
valueListenable: _isSettingsOpenNotifier,
builder: (context, isSettingsOpen, child) {
if (isSettingsOpen) {
return ExcludeSemantics(
child: Listener(
onPointerDown: (_) => _toggleSettings(),
child: const ModalBarrier(dismissible: false),
),
);
} else {
return Container();
}
},
),
Semantics(
sortKey: const OrdinalSortKey(3),
child: ScaleTransition(
alignment: Directionality.of(context) == TextDirection.ltr
? Alignment.topRight
: Alignment.topLeft,
scale: CurvedAnimation(
parent: _settingsPanelController,
curve: Curves.easeIn,
reverseCurve: Curves.easeOut,
),
child: Align(
alignment: AlignmentDirectional.topEnd,
child: Material(
elevation: 7,
clipBehavior: Clip.antiAlias,
borderRadius: BorderRadius.circular(40),
color: Theme.of(context).colorScheme.secondaryContainer,
child: Container(
constraints: const BoxConstraints(
maxHeight: 560,
maxWidth: desktopSettingsWidth,
minWidth: desktopSettingsWidth,
),
child: settingsPage,
),
),
),
),
),
],
_SettingsIcon(
animationController: _iconController,
toggleSettings: _toggleSettings,
isSettingsOpenNotifier: _isSettingsOpenNotifier,
),
_LogoutIcon(
animationController: _iconController,
toggleSettings: _toggleSettings,
isSettingsOpenNotifier: _isSettingsOpenNotifier,
),
],
),
);
}
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: _buildStack,
);
}
}
Code from main project route file
class Path {
const Path(this.pattern, this.builder, {this.openInSecondScreen = false});
/// A RegEx string for route matching.
final String pattern;
/// The builder for the associated pattern route. The first argument is the
/// [BuildContext] and the second argument a RegEx match if that is included
/// in the pattern.
///
/// ```dart
/// Path(
/// 'r'^/demo/([\w-]+)$',
/// (context, matches) => Page(argument: match),
/// )
/// ```
final PathWidgetBuilder builder;
/// If the route should open on the second screen on foldables.
final bool openInSecondScreen;
}
class RouteConfiguration {
/// List of [Path] to for route matching. When a named route is pushed with
/// [Navigator.pushNamed], the route name is matched with the [Path.pattern]
/// in the list below. As soon as there is a match, the associated builder
/// will be returned. This means that the paths higher up in the list will
/// take priority.
static List<Path> paths = [
Path(
r'^' + DemoPage.baseRoute + r'/([\w-]+)$',
(context, match) => DemoPage(slug: match),
openInSecondScreen: false,
),
Path(
r'^' + rally_routes.homeRoute,
(context, match) => StudyWrapper(
study: DeferredWidget(rally.loadLibrary,
() => rally.RallyApp()), // ignore: prefer_const_constructors
),
openInSecondScreen: true,
),
Path(
r'^' + shrine_routes.homeRoute,
(context, match) => StudyWrapper(
study: DeferredWidget(shrine.loadLibrary,
() => shrine.ShrineApp()), // ignore: prefer_const_constructors
),
openInSecondScreen: true,
),
Path(
r'^' + shrine_routes.attendanceRoute,
(context, match) => StudyWrapper(
study: DeferredWidget(
attendance.loadLibrary,
() => attendance
.CampusAttendanceManagementSystem()), // ignore: prefer_const_constructors
),
openInSecondScreen: true,
),
Path(
r'^' + crane_routes.defaultRoute,
(context, match) => StudyWrapper(
study: DeferredWidget(crane.loadLibrary,
() => crane.CraneApp(), // ignore: prefer_const_constructors
placeholder: const DeferredLoadingPlaceholder(name: 'Crane')),
),
openInSecondScreen: true,
),
Path(
r'^' + fortnightly_routes.defaultRoute,
(context, match) => StudyWrapper(
study: DeferredWidget(
fortnightly.loadLibrary,
// ignore: prefer_const_constructors
() => fortnightly.FortnightlyApp()),
),
openInSecondScreen: true,
),
Path(
r'^' + reply_routes.homeRoute,
// ignore: prefer_const_constructors
(context, match) =>
const StudyWrapper(study: reply.ReplyApp(), hasBottomNavBar: true),
openInSecondScreen: true,
),
Path(
r'^' + starter_app_routes.defaultRoute,
(context, match) => const StudyWrapper(
study: starter_app.StarterApp(),
),
openInSecondScreen: true,
),
Path(
r'^/',
(context, match) => const RootPage(),
openInSecondScreen: false,
),
Path(
r'^' + starter_app_routes.loginRoute,
(context, match) => const LoginPage(),
openInSecondScreen: false,
),
];
/// The route generator callback used when the app is navigated to a named
/// route. Set it on the [MaterialApp.onGenerateRoute] or
/// [WidgetsApp.onGenerateRoute] to make use of the [paths] for route
/// matching.
static Route<dynamic>? onGenerateRoute(RouteSettings settings) {
for (final path in paths) {
final regExpPattern = RegExp(path.pattern);
if (regExpPattern.hasMatch(settings.name!)) {
final firstMatch = regExpPattern.firstMatch(settings.name!)!;
final match = (firstMatch.groupCount == 1) ? firstMatch.group(1) : null;
if (kIsWeb) {
return NoAnimationMaterialPageRoute<void>(
builder: (context) => FutureBuilder<bool>(
future: isAuthorized(settings),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!) {
return path.builder(context, match);
}
return LoginPage();
},
),
settings: settings,
);
}
if (path.openInSecondScreen) {
return TwoPanePageRoute<void>(
builder: (context) => FutureBuilder<bool>(
future: isAuthorized(settings),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!) {
return path.builder(context, match);
}
return LoginPage();
},
),
settings: settings,
);
} else {
return MaterialPageRoute<void>(
builder: (context) => FutureBuilder<bool>(
future: isAuthorized(settings),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!) {
return path.builder(context, match);
}
return LoginPage();
},
),
settings: settings,
);
}
}
}
return null;
}
}
Code from package app.dart
class CampusAttendanceManagementSystem extends StatefulWidget {
const CampusAttendanceManagementSystem({super.key});
#override
State<CampusAttendanceManagementSystem> createState() =>
_CampusAttendanceManagementSystemState();
}
class _CampusAttendanceManagementSystemState
extends State<CampusAttendanceManagementSystem> {
final _auth = SMSAuth();
final _navigatorKey = GlobalKey<NavigatorState>();
late final RouteState _routeState;
late final SimpleRouterDelegate _routerDelegate;
late final TemplateRouteParser _routeParser;
#override
void initState() {
/// Configure the parser with all of the app's allowed path templates.
_routeParser = TemplateRouteParser(
allowedPaths: [
'/signin',
'/avinya_types/new',
'/avinya_types/all',
'/avinya_types/popular',
'/avinya_type/:id',
'/avinya_type/new',
'/avinya_type/edit',
'/activities/new',
'/activities/all',
'/activities/popular',
'/activity/:id',
'/activity/new',
'/activity/edit',
'/attendance_marker',
'/#access_token',
],
guard: _guard,
initialRoute: '/signin',
);
_routeState = RouteState(_routeParser);
_routerDelegate = SimpleRouterDelegate(
routeState: _routeState,
navigatorKey: _navigatorKey,
builder: (context) => SMSNavigator(
navigatorKey: _navigatorKey,
),
);
// Listen for when the user logs out and display the signin screen.
_auth.addListener(_handleAuthStateChanged);
super.initState();
}
#override
Widget build(BuildContext context) => RouteStateScope(
notifier: _routeState,
child: SMSAuthScope(
notifier: _auth,
child: MaterialApp.router(
routerDelegate: _routerDelegate,
routeInformationParser: _routeParser,
// Revert back to pre-Flutter-2.5 transition behavior:
// https://github.com/flutter/flutter/issues/82053
theme: ThemeData(
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.linux: FadeUpwardsPageTransitionsBuilder(),
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.windows: FadeUpwardsPageTransitionsBuilder(),
},
),
),
),
),
);
Future<ParsedRoute> _guard(ParsedRoute from) async {
final signedIn = await _auth.getSignedIn();
// String? jwt_sub = campusAttendanceSystemInstance.getJWTSub();
final signInRoute = ParsedRoute('/signin', '/signin', {}, {});
final avinyaTypesRoute =
ParsedRoute('/avinya_types', '/avinya_types', {}, {});
final activitiesRoute = ParsedRoute('/activities', '/activities', {}, {});
final attendanceMarkerRoute =
ParsedRoute('/attendance_marker', '/attendance_marker', {}, {});
// // Go to /apply if the user is not signed in
log("_guard signed in $signedIn");
// log("_guard JWT sub ${jwt_sub}");
log("_guard from ${from.toString()}\n");
if (signedIn && from == avinyaTypesRoute) {
return avinyaTypesRoute;
} else if (signedIn && from == activitiesRoute) {
return activitiesRoute;
} else if (signedIn && from == attendanceMarkerRoute) {
return attendanceMarkerRoute;
}
// Go to /application if the user is signed in and tries to go to /signin.
else if (signedIn && from == signInRoute) {
return ParsedRoute('/avinya_types', '/avinya_types', {}, {});
}
log("_guard signed in2 $signedIn");
// else if (signedIn && jwt_sub != null) {
// return avinyaTypesRoute;
// }
return from;
}
void _handleAuthStateChanged() async {
bool signedIn = await _auth.getSignedIn();
log("_handleAuthStateChanged signed in $signedIn");
if (!signedIn) {
_routeState.go('/signin');
}
}
#override
void dispose() {
_auth.removeListener(_handleAuthStateChanged);
_routeState.dispose();
_routerDelegate.dispose();
super.dispose();
}
}
Please make any suggestions to fix this issue. Thanks in advance

Flutter: feels like my Navigator stack is empty, although if I use Navigator.pop it works well, black screen comes if I use Navigator.popUntil?

Edit: I ended up to make it work, changed the code.
This is how I start the app:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test Demo',
home: LoginPage(),
onGenerateRoute: (settings) {
return CustomPageRouteBuilder.getPageRouteBuilder(Routing.openRoute(settings), settings.name);
},
);
}
}
class CustomPageRouteBuilder {
static PageRouteBuilder getPageRouteBuilder(Widget widget, [String? name]) {
return PageRouteBuilder(
settings: RouteSettings(name: name),
transitionDuration: Duration(
milliseconds: 100,
),
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secAnimation, Widget child) {
animation = CurvedAnimation(
parent: animation,
curve: Curves.elasticIn,
);
return FadeTransition(
opacity: animation,
child: child,
);
},
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secAnimation) {
return Scaffold(
body: Stack(children: [
BiometricAuthentication(widget),
Align(
alignment: Alignment.bottomLeft,
child: Opacity(
opacity: 0.5,
child: Text("Version: ${Constants.buildVersion}",
style: TextStyle(
color: Constants.FONT_COLOR,
fontSize: 12,
)),
),
)
]),
);
});
}
}
Then I let my Routing class handle the routing:
enum Routes {
LOGIN,
FIRST_PAGE,
SECOND_PAGE,
THIRD_PAGE,
}
extension GetRoute on Routes {
String route() {
return '/' + this.toString().split('.').last.toLowerCase().replaceAll('_', '-');
}
}
class Routing {
static openRoute(RouteSettings settings) {
return getRoute(settings);
}
static getRoute(RouteSettings settings) {
String? name = settings.name;
if (name == Routes.LOGIN.route()) {
return LoginPage();
}
if (name == Routes.FIRST_PAGE.route()) {
return FirstPage();
}
if (name == Routes.SECOND_PAGE.route()) {
return SecondPage();
}
if (name == Routes.THIRD_PAGE.route()) {
return ThirdPage();
}
}
}
I get on my ThirdPage on the route of: Login --> FirstPage --> SecondPage --> ThirdPage using the code below:
await Navigator.pushNamed(context, Routes.FIRST_PAGE.route()); // Login
await Navigator.pushNamed(context, Routes.SECOND_PAGE.route()); // FirstPage
await Navigator.pushNamed(context, Routes.THIRD_PAGE.route()); // SecondPage
So I should have 4 pages in my Navigator stack.
Now I try to popUntil Routes.FIRST_PAGE.route(), so the Login and FirstPage should be in the navigator:
Navigator.popUntil(context, ModalRoute.withName(Routes.FIRST_PAGE.route())); // ThirdPage
Here I expect to have the Login -> FirstPage in the stack but everything goes black.
If I use simply the Navigator.pop(context), from ThirdPage it goes back to SecondPage. But in my actuall app I have to go back 5 screens in case a button is used so I'd prefer to not handle that in every page...
Any ideas? Thanks in advance.
switch (settings.name) {
case RouteNames.home:
return MaterialPageRoute(
settings: RouteSettings(name: '/HomePage'),
builder: (_) => HomePage(),
);
};
This is how I set up my pages and return them to onGenerateRoute . I suspect you did not set the route setting name settings: RouteSettings(name: '/HomePage'). This will cause the navigator to not be aware of your route name existing on the stack.

How to set a loading indicator while FutureProvider is not done

I'm using FutureProvider to fetch data from a local db with SQflite, and then render a graph in the Consumer child. However, when loading the app, during a brief period an error is shown :
The following StateError was thrown building Consumer<List<Map<String, dynamic>>>(dirty,
dependencies: [_InheritedProviderScope<List<Map<String, dynamic>>>]):
Bad state: No element
After the graph is rendered fine.
How can I catch this loading state so the error disappears and I can show a CircularProgressIndicator() ?
Parent
FutureProvider<List<Map<String, dynamic>>>(
create: (context) {
return RecordsDatabase.instance.getRecords();
},
catchError: (context, error) {
print("error: ${error.toString()}");
return [];
},
initialData: [],
child: HomeCustom(),
)
Child
#override
Widget build(BuildContext context) {
return Consumer<List<Map<String, dynamic>>>(
builder: (context, records, child) {
GraphState graph =GraphState(records: records, context: context);
return ChangeNotifierProvider<GraphState>(
create: (_) => graph,
child: Scaffold(
backgroundColor: Colors.black,
body: Stack(children: [
Center(
child: graph.records.isEmpty
? Text(
'No Records',
style: TextStyle(color: Colors.white, fontSize: 24),
)
: MyGraph()),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 30, bottom: 50),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _setVisible,
),
),
)
]),
),
);
});
}
}
In the Consumer, check the records value first then return the appropriate widget.
Sample...
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: FutureProvider<List<Map<String, dynamic>>?>(
create: (_) => _getRecords(),
initialData: null,
catchError: (_, __) => <Map<String, dynamic>>[
{'error': 'Something went wrong'}
],
child: HomePage(),
),
);
}
Future<List<Map<String, dynamic>>> _getRecords() async {
final bool isError = false; // set to "true" to check error case
await Future<void>.delayed(const Duration(seconds: 5));
if (isError) {
throw Exception();
}
return <Map<String, dynamic>>[
<String, int>{'item': 1},
<String, String>{'itemTxt': 'one'},
];
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Consumer<List<Map<String, dynamic>>?>(
builder: (_, List<Map<String, dynamic>>? records, __) {
if (records == null) {
return const CircularProgressIndicator();
} else if (records.isNotEmpty &&
records.first.containsKey('error')) {
return Text(records.first['error'] as String);
}
return Text(records.toString());
},
),
),
);
}
}

Flutter returns "No ancestor could be found starting from the context that was passed to I/flutter"

I cannot figure out what is wrong with this code. There is a bloc provider on this page:
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (BuildContext context) {
return DatabaseBloc();
},
),
BlocProvider(
create: (BuildContext context) {
return QuestionholderBloc();
},
)
],`child:RaisedButton(
splashColor: Colors.green[600],
focusElevation: 50.0,
hoverElevation: 50.0,
color: Colors.green[600],
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.yellow[700]),
borderRadius: BorderRadius.circular(50.0)),
child: Text(
"Start",
style: TextStyle(fontWeight: FontWeight.bold),
),
textColor: Colors.white70,
onPressed: () {
ExtendedNavigator.of(context)
.pushNamed(Routes.questionHandler);
}),)
sorry for the possible syntax error.
In the QuestionHolderPage I have:
void didChangeDependencies() {
_statusbloc = BlocProvider.of<QuestionholderBloc>(context);
_dbbloc = BlocProvider.of<DatabaseBloc>(context);
_statusbloc.add(CheckStatus());
super.didChangeDependencies();
}
Widget build(BuildContext context) {
return Scaffold(
body: MultiBlocListener(
listeners: [
BlocListener<QuestionholderBloc, QuestionholderState>(
listener: (context, state) {
if (state is NoQuestions) {
_dbbloc.add(GetQuestions());
} else if (state is HasQuestions) {
_statusbloc.add(GetQuestion());
}
},
),
BlocListener<DatabaseBloc, DatabaseState>(
listener: (context, state) {
if (state is Loaded) {
_statusbloc.add(SetQuestions(questionsToSet: state.questions));
}
},
),
],
child: BlocBuilder<QuestionholderBloc, QuestionholderState>(
builder: (context, state) {
if (state is QuestionLoaded) {
return QuestionWidget(question: state.question);
} else {
return CircularProgressIndicator();
}
},
),
),
);
}
}
Error:
Error: No ancestor could be found starting from the context that was passed to
I/flutter ( 4318): BlocProvider.of().
As can be seen from your code, you are navigating from where you initialise your MultiBlocProvider because of that you are getting this error.
You can not access provider if you initialise in one screen and access it after navigating to other screen.
First way to solve.
If you want to do, so can initialise your MultiBlocProvider above MaterialApp widget, so you can access in each and every screen of your apllication.
Second way:
You can pass all the value which you want to access in other screen when you are navigating and initialise new MultiBlocProvider in that screen.

Could not find the ancestor for consumer provider or provider could not be found

I have a multi provider config and I pass the providers in the mainApp and using the consumerProvider later. But I get the ancestor not found error. The same setup is working for another view but creating problems maybe because of the navigation
I have tried out some options that I found for similar problems in stackoverflow which stated moving the providers across and also looking at the context that is provided but did not find any solutions
First is my Provider.dart file
List<SingleChildCloneableWidget> providers = [
...independentServices,
...dependentServices,
];
List<SingleChildCloneableWidget> independentServices = [
Provider.value(value: FirebaseNewsService()),
Provider.value(value: FirebaseEventsService())
];
List<SingleChildCloneableWidget> dependentServices = [
ProxyProvider<FirebaseNewsService, NewsListModel>(
builder: (context, newsService, _) {
return NewsListModel(newsService: newsService);
}),
ProxyProvider<FirebaseNewsService, NewsCreateModel>(
builder: (context, newsService, _) {
return NewsCreateModel(newsService: newsService);
},
),
ProxyProvider<FirebaseEventsService, EventsListModel>(
builder: (context, eventsService, _) {
return EventsListModel(eventsService: eventsService);
}),
];
Next is the main.dart file
class MainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: providers,
child:MaterialApp(
title: 'MyApp',
initialRoute: RoutePaths.Home,
onGenerateRoute: Router.generateRoute, )
);
}
}
Next is the router.dart file where routing happens
class Router {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
// this is working
case RoutePaths.Home:
return MaterialPageRoute(builder: (context) {
NewsListModel model = Provider.of(context);
return ChangeNotifierProvider<NewsListModel>.value(
value: model, child: NewsPage());
});
break;
case RoutePaths.Events:
return MaterialPageRoute(builder: (_) {
EventListModel model = Provider.of(context);
return ChangeNotifierProvider<EventListModel>.value(
value: model, child: EventsListPage());
});
break;
My homepage file
class NewsPage extends StatelessWidget {
final String _tab1title = allTranslations.text('newsPage.tabtitleone');
final String _tab2title = allTranslations.text('newsPage.tabtitletwo');
static const _tablength = 2;
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tablength,
child: Scaffold(
drawer: Menu(), //Maybe Menu is having a different context
body: NestedScrollView(
...
body: Tabbarview(children: [] . // this works fine
In the problem widget the Events List .dart file
class EventListPage extends statelessWidget {
Widget build(BuildContext context) {
EventListModel model = Provider.of(context);
return Scaffold(drawer: Menu(), appBar: AppBar(), body: ChangeNotifierProvider<EventsListModel>.value(
value: model,
child: Consumer<EventsListModel>(
builder: (context, model, child) => model.busy
? Center(
child: CircularProgressIndicator(),
)
: Column(mainAxisSize: MainAxisSize.max, children: <Widget>[
SmartRefresher(
//key: EventsPageModel.eventsFollowKey,
controller: model.refreshController,
enablePullDown: true,
header: WaterDropMaterialHeader(
backgroundColor: Theme.of(context).primaryColor,
),
enablePullUp: true,
onRefresh: model.onRefresh,
onLoading: model.onLoading,
child: buildchild(model, context)),
]),
),
);
}
I always get could not find ancestor of consumer or
could not find the correct provider .Where I am doing it wrong.
the same thing works for the NewsListModel
Was a case sensitive import bug https://github.com/microsoft/TypeScript/issues/21736 .closing this question