Flutter: Inherited Widget and Routes - dart

I want to have an inherited widget at the root of my application, which will contain my data providers, which I would use throughout the app. So I have this inherited widget, but every time I try to load it I get this The getter 'data' was called on null and I can't figure out why.
So here's my main.dart:
void main() => runApp(new MatAppRoot());
class MatAppRoot extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'MyCoolApp',
routes: <String, WidgetBuilder>{
'Login': (BuildContext context) => new LoginPage(),
'Cool': (BuildContext context) => new CoolPage(),
},
home: new CoolApp(),
);
}
}
class CoolAppextends StatefulWidget {
final Widget child;
CoolApp({this.child});
#override
CoolAppState createState() => new CoolAppState();
static CoolAppState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(CoolInherit) as CoolInherit).data;
}
}
class CoolAppState extends State<CoolApp> {
String randomString = 'AYEEAS!!!';
#override
void initState() { super.initState();
Navigator.of(context).pushNamedAndRemoveUntil('Login', (Route<dynamic> route) => false);
}
#override
Widget build(BuildContext context) {
return new CoolInherit(
data: this,
child: new LoginPage(),
);
}
}
class CoolInherit extends InheritedWidget {
final CoolAppState data;
CoolInherit({
Key key,
this.data,
Widget child,
}): super(
key: key,
child: child
);
#override
bool updateShouldNotify(CoolInherit old) {
return true;
}
}
then my LoginPage basically redirects after the login like this:
if (logInSuccessful) {
Navigator.of(context).pushNamedAndRemoveUntil('Cool', (Route<dynamic> route) => false);
}
In my Cool page I try to load another page when clicking a button like this:
viewCoolDetails() {
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new CoolDetailsPage()),
);
}
but in my CoolDetailsPage it crashes when I do this:
#override
Widget build(BuildContext context) {
final inheritedWidget = CoolApp.of(context);
print(inheritedWidget.randomString); <-- ERROR: The getter 'data' was called on null
return new Text('Cool!');
}
Error Details:
I/flutter ( 6129): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 6129): The following NoSuchMethodError was thrown building CoolDetailsPage(dirty, state:
I/flutter ( 6129): _CoolDetailsPage#ba0bb):
I/flutter ( 6129): The getter 'data' was called on null.
I/flutter ( 6129): Receiver: null
I/flutter ( 6129): Tried calling: data
I/flutter ( 6129):
I/flutter ( 6129): When the exception was thrown, this was the stack:
I/flutter ( 6129): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:46:5)
I/flutter ( 6129): #1 CoolApp.of (/lib/main.dart:56:83)
... etc etc
main.dart:56 is return (context.inheritFromWidgetOfExactType(CoolInherit) as CoolInherit).data; and so if my detective work is up to par, I suspect it is something to with navigations/context, which is preventing my final widget from accessing the inheritedWidget, but I'm not sure about that.
UPDATE:
the best I can tell, I need to insert my InheritedWidget at a higher level; before the navigator. so I inserted this into the MaterialApp:
builder: (context, child) {
return new CoolApp(child: child);
},
but that didn't seen to work...
E/flutter (32321): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter (32321): Navigator operation requested with a context that does not include a Navigator.
E/flutter (32321): The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
E/flutter (32321): #0 Navigator.of.<anonymous closure> (package:flutter/src/widgets/navigator.dart:1180:9)
E/flutter (32321): #1 Navigator.of (package:flutter/src/widgets/navigator.dart:1187:6)

I had the same problem for a long time and I realized that if you wrap the MaterialApp with the Inherited Widget, your data is accessible through the entire app. But in your case, you need to pass data after the user login so to do that you need to create a new Navigator and wrap it with your Inherited Widget. You can see this project https://github.com/johnstef99/inherited-widget-demo
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'InheritedWidget Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyNav(),
);
}
}
Route generatePage(child) {
return MaterialPageRoute(builder: (context) => child);
}
class MyNav extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MyData(
data: 'omg',
child: Navigator(
onGenerateRoute: (settings) {
switch (settings.name) {
case 'page1':
return generatePage(PageOne());
case 'page2':
return generatePage(PageTwo());
case 'page3':
return generatePage(PageThree());
}
},
initialRoute: 'page1',
),
);
}
}
class MyData extends InheritedWidget {
MyData({Key key, this.child, this.data}) : super(key: key, child: child);
final Widget child;
final String data;
static MyData of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(MyData) as MyData);
}
#override
bool updateShouldNotify(MyData oldWidget) {
return true;
}
}
class PageOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 1'),
),
backgroundColor: Colors.red,
body: RaisedButton(
child: Text("Goto page 2, data=${MyData.of(context).data}"),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => PageTwo()));
},
),
);
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 2'),
),
backgroundColor: Colors.red,
body: RaisedButton(
child: Text("Goto page 3, data=${MyData.of(context).data}"),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => PageThree()));
},
),
);
}
}
class PageThree extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 3'),
),
backgroundColor: Colors.green,
body: RaisedButton(
child: Text("Goto page 4, data=${MyData.of(context).data}"),
onPressed: null,
),
);
}
}

That is because you're trying to access CoolApp which is in the route / from another route (dynamic).
But inside your dynamic route, there's no CoolApp. So CoolApp.of(context) returns null, and therefore accessing .data crashes.
You need to find a way to have a CoolApp instance inside your new route.
For more informations, take a look at Get access to the context of InheritedWidget

Related

Flutter - RepaintBoundary causes state reset of StatefulWidget

I have a preview widget that loads data after a user tap. This state (already tapped or not) should not be lost while scrolling (the preview is located in a list) or navigating through other screen.
The scrolling is solved by adding AutomaticKeepAliveClientMixin which saves the state when scrolling away.
Now i also need to wrap the preview widget (actually a more complex widget that contains the preview) with a RepaintBoundary, to be able to make a "screenshot" of this widget alone.
Before i wrap the widget with a RepaintBoundary, the state is saved both while scrolling and navigating to another screen.
After i add the RepaintBoundary the scrolling still works but for navigation the state is reset.
How can i wrap a Stateful widget that should hold its state with a RepaintBoundary?
Code is a simplified example of my implementation with the same problem.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final title = 'Test';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: TestList(40),
),
);
}
}
class TestList extends StatefulWidget {
final int numberOfItems;
TestList(this.numberOfItems);
#override
_TestListState createState() => _TestListState();
}
class _TestListState extends State<TestList> {
#override
Widget build(BuildContext context) {
print('_TestListState build.');
return ListView.builder(
itemCount: widget.numberOfItems,
itemBuilder: (context, index) {
return RepaintBoundary(
key: GlobalKey(),
child: Preview()
);
},
);
}
}
class Preview extends StatefulWidget {
#override
_PreviewState createState() => _PreviewState();
}
class _PreviewState extends State<Preview> with AutomaticKeepAliveClientMixin {
bool loaded;
#override
void initState() {
super.initState();
print('_PreviewState initState.');
loaded = false;
}
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
print('_PreviewState build.');
if(loaded) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewScreen()),
);
},
child: ListTile(
title: Text('Loaded. Tap to navigate.'),
leading: Icon(Icons.visibility),
),
);
} else {
return GestureDetector(
onTap: () {
setState(() {
loaded = true;
});
},
child: ListTile(
title: Text('Tap to load.'),
),
);
}
}
}
class NewScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('New Screen')),
body: Center(
child: Text(
'Navigate back and see if loaded state is gone.',
style: TextStyle(fontSize: 14.0),
),
),
);
}
}
Take a look at RepaintBoundary.wrap, it assigns the RepaintBoundary widget a key based on its child or childIndex so state is maintained:
class _TestListState extends State<TestList> {
#override
Widget build(BuildContext context) {
print('_TestListState build.');
return ListView.builder(
itemCount: widget.numberOfItems,
itemBuilder: (context, index) {
return RepaintBoundary.wrap(
Preview(),
index,
);
},
);
}
}
https://api.flutter.dev/flutter/widgets/RepaintBoundary/RepaintBoundary.wrap.html
EDIT: As per the below comments, it looks like this solution would break the screenshot ability so you'd have to store the list of children widgets in your state like so:
class _TestListState extends State<TestList> {
List<Widget> _children;
#override
void initState() {
super.initState();
_children = List.generate(
widget.numberOfItems,
(_) => RepaintBoundary(
key: GlobalKey(),
child: Preview(),
));
}
#override
Widget build(BuildContext context) {
print('_TestListState build.');
return ListView(children: _children);
}
}

How to use showModalBottomSheet in StatelessWidget?

I try to code like this
showModalBottomSheet(
context: context, // I got error here (Undefined name 'context'.dart(undefined_identifier))
builder: (context){
return Container(
);
);
I got error on context: context, error message is
Undefined name 'context'.dart(undefined_identifier)
This is how you call the showModalBottomSheet in a StatelessWidget
class TestStatelessWidget extends StatelessWidget{
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('Press me'),
onPressed: ()=>showPress(context),
);
}
void showPress(BuildContext context){
showModalBottomSheet(context:context, builder: (context){
return Text('hello');
});
}
}
You are getting this error because you don't have access to context everywhere in a StatelessWidget class. All you need to do is pass context from build() if you are using this outside build() method else use it within build() method.
Solution 1. (Using inside build())
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("Show sheet"),
onPressed: () {
showModalBottomSheet(context: context, builder: (context) => YourWidget());
},
);
}
Solution 2. (Using outside build())
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("Show sheet"),
onPressed: () => _showSheet(context),
);
}
void _showSheet(BuildContext context) {
showModalBottomSheet(context: context, builder: (context) => YourWidget());
}
You did not close your showModalBottomSheet's builder correctly.
There is a } missing.
This would be the correct usage:
showModalBottomSheet(
context: context,
builder: (builder){
return Container();
}
);

Dispose widget when navigating to new route

I have two screens in my app.
Screen A runs a computationally expensive operation while opened, and properly disposes by cancelling animations/subscriptions to the database when dispose() is called to prevent memory leaks.
From Screen A, you can open another screen (Screen B).
When I use Navigator.pushNamed, Screen A remains in memory, and dispose() is not called, even though Screen B is now shown.
Is there a way to force disposal of Screen A when it is not in view?
Example code where first route is never disposed:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Navigation Basics',
home: FirstRoute(),
));
}
class FirstRoute extends StatefulWidget {
#override
_FirstRouteState createState() => _FirstRouteState();
}
class _FirstRouteState extends State<FirstRoute> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: RaisedButton(
child: Text('Open route'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
);
}
#override
void dispose() {
// Never called
print("Disposing first route");
super.dispose();
}
}
class SecondRoute extends StatefulWidget {
#override
_SecondRouteState createState() => _SecondRouteState();
}
class _SecondRouteState extends State<SecondRoute> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
);
}
#override
void dispose() {
print("Disposing second route");
super.dispose();
}
}
I know it's a bit late but I think you should override the deactivate method. Since we are changing the page we are not actually destroying it, that's why the dispose isn't being called.
If you'd like more information this page lists the lifecycle of the stateful widgets.
From the link:
'deactivate()' is called when State is removed from the tree, but it might be
reinserted before the current frame change is finished. This method exists basically
because State objects can be moved from one point in a tree to another.
call Navigator.pushReplacement when routing between first and second screen.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Navigation Basics',
home: FirstRoute(),
));
}
class FirstRoute extends StatefulWidget {
#override
_FirstRouteState createState() => _FirstRouteState();
}
class _FirstRouteState extends State<FirstRoute> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: RaisedButton(
child: Text('Open route'),
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
);
}
#override
void dispose() {
// Never called
print("Disposing first route");
super.dispose();
}
}
class SecondRoute extends StatefulWidget {
#override
_SecondRouteState createState() => _SecondRouteState();
}
class _SecondRouteState extends State<SecondRoute> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: RaisedButton(
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => FirstRoute()),
);
},
child: Text('Go back!'),
),
);
}
#override
void dispose() {
print("Disposing second route");
super.dispose();
}
}
Try this
In flutter new versions deactivate won't be called when you push a new widget on top of another widget. Also there is an open issue related to this topic on flutter github: https://github.com/flutter/flutter/issues/50147
The best way to handle this issue is to add RouteObserver<PageRoute> to your material app and override didPushNext and didPushNext functions.
There is a very helpful medium article related to this topic which you can find here: https://medium.com/koahealth/how-to-track-screen-transitions-in-flutter-with-routeobserver-733984a90dea
As Article said create your own RouteAwareWidget, you can add these two call backs to the fields of the widget:
didPopNext
didPushNext
class RouteAwareWidget extends StatefulWidget {
final Widget child;
final VoidCallback? didPopNext;
final VoidCallback? didPushNext;
const RouteAwareWidget({
Key? key,
required this.child,
this.didPopNext,
this.didPushNext,
}) : super(key: key);
#override
State<RouteAwareWidget> createState() => RouteAwareWidgetState();
}
class RouteAwareWidgetState extends State<RouteAwareWidget> with RouteAware {
#override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
}
#override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
#override
void didPush() {}
#override
void didPopNext() {
dPrint('didPopNext');
widget.didPopNext == null ? null : widget.didPopNext!();
super.didPopNext();
}
#override
void didPushNext() {
dPrint('didPushNext');
widget.didPushNext == null ? null : widget.didPushNext!();
super.didPushNext();
}
#override
Widget build(BuildContext context) => widget.child;
}
Create a global RouteObserver<PageRoute> and add it to your material app:
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
MaterialApp(
navigatorObservers: [routeObserver],
debugShowCheckedModeBanner: false,
routes: _routes,
)
then in your routs you should wrap your routes with RouteAwareWidget and add custom function you want:
final _routes = {
HomePage.routeName: (context) => RouteAwareWidget(
child: const HomePage(),
didPushNext: () => sl<CameraBloc>().add(Dispose()),
didPopNext: () => sl<CameraBloc>().add(Init()),
),
MyQuestions.routeName: (context) => const RouteAwareWidget(
child: MyQuestions(),
),
};
didPushNext will be called when you push a widget on top of HomePage and didPopNext will be called when you pop the last widget above HomePage.
With Navigator.pushReplacement(), if using MaterialPageRoute, then setting
maintainState:false
will ensure that dispose() is called.
A light weight solution for a single route case is using a callback function triggered from the SecondRoute.
Trigger the callback from the WidgetsBinding.instance.addPostFrameCallback() within the initState() on the SecondRoute
More information on WidgetsBinding and when they run can be found here: Flutter: SchedulerBinding vs WidgetsBinding.
WidgetsBinding & SchedulerBinding will be printed only once as we called it initState(), but it will be called when build method finished it’s rendering.
import 'package:flutter/material.dart';
class FirstRoute extends StatefulWidget {
const FirstRoute({super.key});
#override
State<FirstRoute> createState() => _FirstRouteState();
}
class _FirstRouteState extends State<FirstRoute> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Screen A')),
body: Center(
child: TextButton(
child: const Text('Go to Screen B'),
onPressed: () async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => SecondRoute(_callbackFn),
),
);
_secondRouteDone();
},
),
),
);
}
_callbackFn() {
print("Widget B Loaded, Free up memory, dispose things, etc.");
}
_secondRouteDone() {
print("SecondRoute Popped, Reinstate controllers, etc.");
}
}
class SecondRoute extends StatefulWidget {
final Function() notifyIsMountedFn;
const SecondRoute(this.notifyIsMountedFn, {super.key});
#override
State<SecondRoute> createState() => _SecondRouteState();
}
class _SecondRouteState extends State<SecondRoute> {
#override
void initState() {
super.initState();
// Notify FirstRoute after paint
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.notifyIsMountedFn();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Screen B')),
);
}
}
Not only to call 'deactivate()' but also to use 'Navigator.pushReplacement()' for page moving is necessary. Not working if you are using 'Navigator.push()'.

Flutter scoped_model not find when try to access with Navigator page

I am using flutter scoped_model, When we try to access ScopedModel from child Widget which able to access without error.
But same code was not working with when i try to access it from Widget which load using Navigator.push, it gives error Error: Could not find the correct ScopedModel.
PageModel declare at top page.
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
class PageModel extends Model {
String title;
static PageModel of(BuildContext context) =>
ScopedModel.of<PageModel>(context);
loadTitle() {
title = 'Old Title ';
}
updateTitle() {
title = 'New Title';
notifyListeners();
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final PageModel model = PageModel();
#override
Widget build(BuildContext context) {
return ScopedModel<PageModel>(
model: model,
child: Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: HomePageBody(),
),
);
}
}
class HomePageBody extends StatefulWidget {
#override
_HomePageBodyState createState() => _HomePageBodyState();
}
class _HomePageBodyState extends State<HomePageBody> {
#override
void initState() {
PageModel.of(context).loadTitle();
super.initState();
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<PageModel>(
builder: (BuildContext context, child, PageModel model) {
return Column(
children: <Widget>[
Text(model.title),
RaisedButton(
child: Text('Edit'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPage(), fullscreenDialog: true),
);
},
),
],
);
});
}
}
class DetailPage extends StatefulWidget {
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Page'),
),
body: RaisedButton(
child: Text('Update'),
onPressed: () {
PageModel.of(context).updateTitle();
},
),
);
}
}
From DetailPage when we call PageModel.of(context).updateTitle(); following error coming,
/flutter (26710): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (26710): The following ScopedModelError was thrown while handling a gesture:
I/flutter (26710): Error: Could not find the correct ScopedModel.
I/flutter (26710):
I/flutter (26710): To fix, please:
I/flutter (26710):
I/flutter (26710): * Provide types to ScopedModel<MyModel>
I/flutter (26710): * Provide types to ScopedModelDescendant<MyModel>
I/flutter (26710): * Provide types to ScopedModel.of<MyModel>()
I/flutter (26710): * Always use package imports. Ex: `import 'package:my_app/my_model.dart';
I/flutter (26710):
I/flutter (26710): If none of these solutions work, please file a bug at:
I/flutter (26710): https://github.com/brianegan/scoped_model/issues/new
What you’re doing is trying to find a PageModel of that context which there is none, since you haven’t created any for that specific widget context.
What you want to wrap your RaisedButton in a ScopedModelDescendant<PageModel> and update your model by using the model.updateTitle() instead.
That will look for the closest PageModel ancestor in the tree.

InheritedWidget - The getter was called on null after navigator.push

I'm having trouble trying to access an InheritedWidget after navigating to a new widget.
I have my top level widget like this
class App extends StatelessWidget{
build(context){
return MaterialApp(
title: 'Iniciar Sesion',
home: LoginBlocProvider(child: WelcomeScreen()),
);
}
}
Then WelcomeScreen has a button to navigate to LoginScreen
class WelcomeScreen extends StatelessWidget {
#override Widget build(BuildContext context){
return Scaffold(
body: Center(child: MyButton)
);
}
}
class MyButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
shape: StadiumBorder(),
child: Text('Ingresar', style: TextStyle(color: Colors.black)),
elevation: 5.0,
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>LoginScreen()
));
}
);
}
}
Finally in LoginScreen I want to access the InheritedWidget
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
LoginBloc bloc;
#override void didChangeDependencies() {
bloc = LoginBlocProvider.of(context);
super.didChangeDependencies();
}
#override Widget build(BuildContext context){
return Scaffold(
body:
Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: 0.0,
child: Image.asset('assets/images/img.jpg',
fit: BoxFit.none,
),
),
_buildLogin(),
],
),
);
}
}
Edited: Here it's the LoginBlocProvider
class LoginBlocProvider extends InheritedWidget {
final bloc;
LoginBlocProvider({Key key, Widget child})
: bloc = LoginBloc(),
super(key:key, child:child);
#override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static LoginBloc of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(LoginBlocProvider) as LoginBlocProvider).bloc;
}
}
But, when I run the .of method of the InheritedWidget I get this error
I/flutter (27725): The following NoSuchMethodError was thrown building Builder:
I/flutter (27725): The getter 'bloc' was called on null.
I/flutter (27725): Receiver: null
I/flutter (27725): Tried calling: bloc
I have the impression that it all has to do with the context in the Navigator.push builder method.
Because if I use the LoginScreen widget without the Navigator.push, I can use the InheritedWidget perfectly fine
The error is happening because the context passed to the LoginBlocProvider.of() method is not finding the instance.
Any thoughts on this?
In the code you've provided, LoginScreen is not a descendant of LoginBlocProvider which is why it can't find the ancestor widget. Your code wraps the WelcomeScreen route in LoginBlocProvider, but not the whole navigator. The solution is to wrap your MaterialApp in LoginBlocProvider and then you will have access to it everywhere in your app.
class App extends StatelessWidget {
#override
Widget build(context) {
return LoginBlocProvider(
child: MaterialApp(
title: 'Iniciar Sesion',
home: WelcomeScreen(),
),
);
}
}
InheritedWidget should wrap the full MatrialApp widget (root widget)

Resources