how can i pass a variable to a class and call that variable in any other screen without it being reset - dart

i want to be able to call an empty variable from a class, assign a value to it and make it persistent, anything aside provider e.t.c would be help, i don't want to overhaul the entire app again to do some bloc, provider e.t.c
NB: all screens are stateful widgets
i have tried creating a class with an empty string and passing a value to it from another screen, but this doesn't seem to work
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
class MethodA {
// id(user, context){
// var name =user.email;
// }
String identity;
MethodA({this.iD});
bool isLoggedIn() {
if (FirebaseAuth.instance.currentUser() != null) {
return true;
} else {
return false;
}
}
Future<void> addUserA( userinfo) async {
//this.iD=id;
Firestore.instance
.collection('user')
.document('furtherinfo').collection(identity).document('Personal Info')
.setData(userdoc)
.catchError((e) {
print(e);
});
}
each time i pass the argument to i.e foo='bar';
and i import that class in another screen, i.e screen 9, foo is automatically set to null, but i would want foo to be bar

I would suggest that you use the Provider since it is the easiest way for me to manage state throughout the app. Flutter starts with one component on top of the widget tree so i would place my provider here.
Example
void main() {runApp(MyApp());}
class MyApp extends StatelessWidget {
MyApp();
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<FirebaseUser>.value(
stream: FirebaseAuth.instance.onAuthStateChanged, // Provider to manage user throughout the app.
),
],
child: MaterialApp(
title: 'My App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.green,
primarySwatch: Colors.green,
accentColor: Colors.yellow,
),
home: MainPage(),
),
);
}
}
Then in your class you can do the following
class MethodAService with ChangeNotifier {
String _identity = null;
FirebaseUser _user = null;
// constructor with the (new changes )
MethodAService(FirebaseUser user){
this._user = user;
}
get identity => _identity ;
setIdentity(String identity) {
_identity = identity ;
notifyListeners(); // required to notify the widgets of your change
}
}
Then when you want to use it anywhere in your app just do the following in the build method
#override
Widget build(BuildContext context) {
final user = Provider.of<FirebaseUser>(context); // to get the current user
final methodA = Provider.of<MethodAService>(context); // get your service with identity
// now you can set the string using
methodA.setIdentity('new identity');
// or just use it like this
if(methodA.identity.isNotEmpty()){
print(methodA.identity);
}else{
print('Identity is empty');
}
return ChangeNotifierProvider<MethodAService>(
builder: (context) => MethodAService(user), // Your provider to manage your object, sending the Firebase user in
child: loggedIn ? HomePage() : LoginPage(), );
}
References
Provider Package
Fireship 185 Provider
Great Youtube video explaining the code
Update for comment
For getting the user uid you can just do user.uid
Changed code above to fit the

I'm not sure put the whole app in a StreamProvider is the best choice. That means the app will be rebuilt on each stream value.
To make a Widget available on all screens, you need a TransitionBuilder in your MaterialApp.
To avoid the external dependency you can also use an InheritedWidget
signed_user.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class SignedUser extends InheritedWidget {
final FirebaseUser user;
SignedUser({#required this.user, #required Widget child})
: super(child: child);
#override
bool updateShouldNotify(SignedUser oldWidget) => true;
static SignedUser of(BuildContext context) =>
context.inheritFromWidgetOfExactType(SignedUser);
}
my_transition_builder.dart
class MyTransitionBuilder extends StatefulWidget {
final Widget child;
const MyTransitionBuilder({Key key, this.child}) : super(key: key);
#override
_MyTransitionBuilderState createState() => _MyTransitionBuilderState();
}
class _MyTransitionBuilderState extends State<MyTransitionBuilder> {
StreamBuilder<FirebaseUser> _builder;
#override
void initState() {
super.initState();
_builder = StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (context, snapshot) {
return SignedUser(
child: widget.child,
user: snapshot.data,
);
});
}
#override
Widget build(BuildContext context) {
return _builder;
}
}
main.dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// this will make your inherited widget available on all screens of your app
builder: (context, child) {
return MyTransitionBuilder(child: child);
},
routes: {
'/editAccount': (context) => new EditAccountPage(),
},
theme: ThemeData(
primarySwatch: Colors.green,
),
home: MyHomePage(),
);
}
}
usage in edit_account_page.dart
#override
Widget build(BuildContext context) {
var user = SignedUser.of(context).user;
return Scaffold(
body: FutureBuilder<DocumentSnapshot>(
future: Firestore.instance.document('users/${user.uid}').get(),

Related

bloc does not get initialized when using generic bloc provider

Here is the bloc (simplified):
import 'package:autobleidas_flutter/bloc/bloc_base.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:rxdart/rxdart.dart';
class LoginBloc extends BlocBase {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final PublishSubject<bool> loggedIn = PublishSubject<bool>();
final PublishSubject<bool> loading = PublishSubject<bool>();
}
Here is the bloc provider:
class BlocProvider<T> extends InheritedWidget {
final T bloc;
BlocProvider({Key key, Widget child, this.bloc})
: super(key: key, child: child);
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
return (context.inheritFromWidgetOfExactType(type) as BlocProvider).bloc;
}
static Type _typeOf<T>() => T;
#override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
However, in the LoginScreen I cannot access the loggedIn Subject of the bloc. Here is how LoginScreen is opened from main and the bloc is passed to it:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: allTranslations.supportedLocales(),
home: BlocProvider<LoginBloc>(child: LoginScreen()), // <-------- HERE
);
}
}
Here is how I try to access it in the LoginScreen:
class _LoginScreenState extends State<LoginScreen> {
bool _isLoading = false;
#override
void didChangeDependencies() {
LoginBloc bloc = BlocProvider.of<LoginBloc>(context);
bloc.loggedIn.listen((isLoggedIn) => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => RegistrationScreen())));
bloc.loading.listen((state) => setState(() => _isLoading = state));
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Container();
}
the error:
The getter 'loggedIn' was called on null.
So why is the bloc null? How do I fix this?
In this line, BlocProvder expect a bloc.
home: BlocProvider<LoginBloc>(child: LoginScreen()),
You are not passing your bloc here.
Pass it like below:
home: BlocProvider<LoginBloc>(child: LoginScreen(),bloc: LoginBloc()),
BlocProvider<LoginBloc> means your defining a type of the bloc you are going to pass.

How can I use BottomNavigationBar with BLoC?

When I use BottomNavigationBar with BLoC pattern, it causes the error, Bad state: Stream has already been listened to.
I may listen a stream of a BLoC at just one place.
My code is the following.
main.dart
import 'package:flutter/material.dart';
import 'package:bottom_tab_bloc/app_state_bloc.dart';
import 'package:bloc_provider/bloc_provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return BlocProvider(
creator: (context, _bag) => AppStateBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String activeTab = "tab1";
final bottomTabs = ["tab1", "tab2"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(activeTab),
),
body: buildTab(activeTab),
bottomNavigationBar: BottomNavigationBar(
currentIndex: bottomTabs.indexOf(activeTab),
onTap: (int index) {
setState(() {
activeTab = bottomTabs[index];
});
},
items: bottomTabs.map((tab) =>
buildBnbItem(tab)
).toList(),
),
);
}
Widget buildTab(String tab) {
if (tab == "tab1") {
return TabOne();
} else if (tab == "tab2") {
return TabTwo();
}
}
BottomNavigationBarItem buildBnbItem (String tab) {
assert(bottomTabs.contains(tab));
if (tab == "tab1") {
return BottomNavigationBarItem(
title: Text('Tab1'),
icon: Icon(Icons.looks_one),
);
} else if (tab == "tab2") {
return BottomNavigationBarItem(
title: Text('Tab1'),
icon: Icon(Icons.looks_two),
);
}
}
}
class TabOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
final AppStateBloc bloc = BlocProvider.of<AppStateBloc>(context);
return StreamBuilder(
stream: bloc.outValue1,
builder: (context, snapshot) =>
Center(child: Text(snapshot.data.toString())),
);
}
}
class TabTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
final AppStateBloc bloc = BlocProvider.of<AppStateBloc>(context);
return StreamBuilder(
stream: bloc.outValue2,
builder: (context, snapshot) =>
Center(child: Text(snapshot.data.toString())),
);
}
}
app_state_bloc.dart
import 'dart:async';
import 'package:bloc_provider/bloc_provider.dart';
class AppStateBloc implements Bloc {
StreamController<int> _value1Controller
= StreamController<int>();
Sink<int> get _inValue1 => _value1Controller.sink;
Stream<int> get outValue1 => _value1Controller.stream;
StreamController<int> _updateValue1Controller
= StreamController<int>();
Sink<int> get updateValue1 =>
_updateValue1Controller.sink;
StreamController<int> _value2Controller
= StreamController<int>();
Sink<int> get _inValue2 => _value2Controller.sink;
Stream<int> get outValue2 => _value2Controller.stream;
StreamController<int> _updateValue2Controller
= StreamController<int>();
Sink<int> get updateValue2 =>
_updateValue2Controller.sink;
AppStateBloc(){
_inValue1.add(1);
_updateValue1Controller.stream.listen(_updateValue1);
_inValue2.add(2);
_updateValue2Controller.stream.listen(_updateValue2);
}
#override
void dispose() {
_value1Controller.close();
_updateValue1Controller.close();
_value2Controller.close();
_updateValue2Controller.close();
}
void _updateValue1(int value1) {
_inValue1.add(value1);
}
void _updateValue2(int value2) {
_inValue2.add(value2);
}
}
I can go to the TabTwo from TabOne only at the first time, but the error occurs when I go back to TabOne .
I also tried using StreamController<int>.broadcast() in app_state_bloc.dart, but snapshot.data is always null.
How can I implement BottomNavigationBar with BLoC pattern?
Why the streams are called more than twice, though I write each stream at just one place?
Is AppStateBloc.dispose() is called in this code? When and where AppStateBloc.dispose() is called?
Why broadcast stream's snapshot.data is always null?
Why the streams are called more than twice, though I write each stream
at just one place?
The error is related to the number of subscribers to the stream. StreamController by default only allows one subscriber. That's why TabOne works fine the first time, but breaks afterwards.
Is AppStateBloc.dispose() is called in this code? When and where
AppStateBloc.dispose() is called?
It would be called when the BlocProvider widget gets removed, but since it's being used as the app root, I guess this only happens when the app is closed.
Why broadcast stream's snapshot.data is always null?
Broadcast streams do not buffer events when there is no listener. Since you're writing to the stream before TabOne is created, the event is lost and you get null.
How can I implement BottomNavigationBar with BLoC pattern?
I guess it depends on your use case, but for this particular example, if you replace StreamController with rxdart's BehaviorSubject, it works fine, because then you'd have a broadcast stream that always sends you the last event.

How to prevent passing down BuildContext?

Currently I get the BuildContext from the build method in HomeScreen, and then I have to pass it down to _gridSliver then down to _storeCard.
How can I write the code so that I don't need to pass the context down?
Maybe I can create a new private StatelessWidget called _StoreCard that will have its own build method and thus its own BuildContext?
class HomeScreen extends StatelessWidget {
HomeScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, List<MyStore.Store>>(
converter: (Store<AppState> store) => store.state.home.stores,
builder: (BuildContext context, List<MyStore.Store> stores) =>
CustomScrollView(slivers: <Widget>[_gridSliver(stores, context)]));
}
Widget _gridSliver(stores, context) {
return SliverGrid(
delegate: SliverChildListDelegate(List<Widget>.from(stores.map(_storeCard, context))));
}
Widget _storeCard(MyStore.Store store, BuildContext context) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => StoreScreen(storeId: store.id)),
);
},
child: Container(child: Text(store.name))
);
}
}
Another instance of this problem is I navigate on a child function.
#override
Widget build(BuildContext context) {
return Column(
children: [
WhiteButton(text: "Login with Facebook", onPressed: _loginWithFacebook),
WhiteButton(text: "Login with Google", onPressed: _loginWithGoogle),
])
)
}
_loginWithFacebook(context) async {
...
var user = User.fromFacebook(result.accessToken.token, json.decode(graphResponse.body));
await _login(user, context);
}
}
_loginWithGoogle(context) async {
...
GoogleSignInAccount googleUser = await _googleSignIn.signIn();
await _login(User.fromGoogle(googleUser), context);
}
_login(user, context) async {
var fetchedUser = await MeService.getUser(user);
if (fetchedUser != null) {
loginSuccess(fetchedUser);
Navigator.popUntil(context, ModalRoute.withName(MainRoutes.root));
} else {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => RegisterScreen(user: user)),
);
}
}
To get a new BuildContext, you have two main solutions:
Extract part of the subtree into a new widget, typically StatelessWidget. And then use it's BuildContext from the build method
Use Builder widget, which is basically a reusable widget made to obtain a BuildContext:
Example:
#override
Widget build(BuildContext context) {
return Builder(
builder: (context) {
// do something with this new context
},
);
}
You have to use a Bloc pattern that uses an Inherited Widget, but still you'll have to pass context, but in a more straight forward way. I recommend using this app by Stephen Grider, to figure out how the whole thing works. He explains in his tutorial how to put the whole thing together but I can't link you to that because that would be advertising.
The idea is, you first create a file Bloc.dart that is going to contain your logic, then you create what is called a Provider, in a Provider.dart.
Provider.dart:
class Provider extends InheritedWidget {
final bloc = Bloc();
Provider({Key key, Widget child}) : super(key: key, child: child);
bool updateShouldNotify(_) => true;
static Bloc of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
}
}
In your file that contains the Material App, you wrap the material App with the provider:
Widget build(BuildContext context) {
return Provider(
child: MaterialApp(
And then you use the provider in every other class down the three of widgets.
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final bloc = Provider.of(context); // this is where you insert the provider
return StoreConnector<AppState, List<MyStore.Store>>(
converter: (Store<AppState> store) => store.state.home.stores,
builder: (BuildContext context, List<MyStore.Store> stores) =>
CustomScrollView(slivers: <Widget>[_gridSliver(stores, context)]));
}
Widget _gridSliver(stores) {
final bloc = Provider.of(context);
return SliverGrid(
delegate: SliverChildListDelegate(List<Widget>.from(stores.map(_storeCard, context))));
}
Widget _storeCard(MyStore.Store store) {
final bloc = Provider.of(context);
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => StoreScreen(storeId: store.id)),
);
},
child: Container(child: Text(store.name))
);
}
}
I'm a total noob with flutter and take everything with grain of salt, but this is what I would use. Hope it helps.

How to load theme at beginning in Flutter

I want to users can change and save the theme color in my app. However, I have no ideas how to load the saved theme color when the app starts running. For example, I want to load the saved theme color directly in the comment place below. I tried SharedPreference. However, the SharedPreference instance needs to run with await. It seems can't be used here. Is there any way I can load the saved theme here directly instead of using setState or something like it?
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: // how to load saved theme here?
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
This answer goes a bit further. It shows how to load and save theme preferences, how to build a ThemeData, and how to change the theme from a page of your app.
Save the user preferences (which theme is selected) using the shared_preferences plugin.
Use the "controller pattern" that is used throughout the Flutter framework to provide the currently selected theme (and changes to it) to your app.
Use an InheritedWidget to use the controller in any part of your app.
Here is how the controller looks like:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// provides the currently selected theme, saves changed theme preferences to disk
class ThemeController extends ChangeNotifier {
static const themePrefKey = 'theme';
ThemeController(this._prefs) {
// load theme from preferences on initialization
_currentTheme = _prefs.getString(themePrefKey) ?? 'light';
}
final SharedPreferences _prefs;
String _currentTheme;
/// get the current theme
String get currentTheme => _currentTheme;
void setTheme(String theme) {
_currentTheme = theme;
// notify the app that the theme was changed
notifyListeners();
// store updated theme on disk
_prefs.setString(themePrefKey, theme);
}
/// get the controller from any page of your app
static ThemeController of(BuildContext context) {
final provider = context.inheritFromWidgetOfExactType(ThemeControllerProvider) as ThemeControllerProvider;
return provider.controller;
}
}
/// provides the theme controller to any page of your app
class ThemeControllerProvider extends InheritedWidget {
const ThemeControllerProvider({Key key, this.controller, Widget child}) : super(key: key, child: child);
final ThemeController controller;
#override
bool updateShouldNotify(ThemeControllerProvider old) => controller != old.controller;
}
Here is how you would use the controller and InheritedWidget in your app:
void main() async {
// load the shared preferences from disk before the app is started
final prefs = await SharedPreferences.getInstance();
// create new theme controller, which will get the currently selected from shared preferences
final themeController = ThemeController(prefs);
runApp(MyApp(themeController: themeController));
}
class MyApp extends StatelessWidget {
final ThemeController themeController;
const MyApp({Key key, this.themeController}) : super(key: key);
#override
Widget build(BuildContext context) {
// use AnimatedBuilder to listen to theme changes (listen to ChangeNotifier)
// the app will be rebuilt when the theme changes
return AnimatedBuilder(
animation: themeController,
builder: (context, _) {
// wrap app in inherited widget to provide the ThemeController to all pages
return ThemeControllerProvider(
controller: themeController,
child: MaterialApp(
title: 'Flutter Demo',
theme: _buildCurrentTheme(),
home: MyHomePage(),
),
);
},
);
}
// build the flutter theme from the saved theme string
ThemeData _buildCurrentTheme() {
switch (themeController.currentTheme) {
case "dark":
return ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.orange,
);
case "light":
default:
return ThemeData(
brightness: Brightness.light,
primarySwatch: Colors.blue,
);
}
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
// thanks to the inherited widget, we can access the theme controller from any page
ThemeController.of(context).setTheme('light');
},
child: Text('Light Theme'),
),
RaisedButton(
onPressed: () {
ThemeController.of(context).setTheme('dark');
},
child: Text('Dark Theme'),
)
],
),
),
);
}
}
You have a few options as to how you'd load it. The first is as Gunter said in a comment - you make MyApp into a stateful widget and load it with initState(), then setState it.
That would look something like this:
class MyApp extends StatefulWidget {
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
ThemeData theme = ThemeData.dark(); // whatever your default is
#override
void initState() {
super.initState();
SharedProperties.getInstance().then((prefs) {
ThemeData theme = ThemeData.light(); // load from prefs here
setState(() => this.theme = theme);
});
}
...
}
The second option is to use a FutureBuilder.
class MyApp extends StatelessWidget {
final Future<ThemeData> loadThemeData = SharedPreferences.getInstance().then((prefs) {
... get theme from prefs
return ThemeData.light();
});
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: loadThemeData,
builder: (context, snapshot) {
return MaterialApp(
theme: snapshot.data,
);
},
initialData: ThemeData.dark(), // whatever you want your default theme to be
);
}
}
The third option is to do the loading before you actually start your app - in your main method. I don't know if this is really recommended as if sharedpreferences takes a while it could delay the start of your app, but realistically it should be very quick and you probably want to avoid a flash different theme showing anyways.
main() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
ThemeData theme = ThemeData.dark(); // get theme from prefs
runApp(MyApp(
theme: theme,
));
}
class MyApp extends StatelessWidget {
final ThemeData theme;
const MyApp({Key key, #required this.theme}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: theme,
....
);
}
}
Load theme data from local storage in main function as await

Flutter close a Dialog inside a condition

I am trying to close a Dialog dynamically.
What I am actually trying to do is to change the content of the dialog depending on the information I have at the moment.
Starts with loading info and no button and after a few seconds could be an error with the OK button to close the Dialog Box.
class Dialogs{
loginLoading(BuildContext context, String type, String description){
var descriptionBody;
if(type == "error"){
descriptionBody = CircleAvatar(
radius: 100.0,
maxRadius: 100.0,
child: new Icon(Icons.warning),
backgroundColor: Colors.redAccent,
);
} else {
descriptionBody = new Center(
child: new CircularProgressIndicator(),
);
}
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
return AlertDialog(
title: descriptionBody,
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Center(child: Text(description))
],
),
),
);
}
);
}
}
So after creating the instance os the dialog and opening it
Dialogs _dialog = new Dialogs();
_dialog.loginLoading(context, "loading", "loading...");
// Close the dialog code here
don't know how to do it
// Call again the AlertDialog with different content.
https://docs.flutter.io/flutter/material/showDialog.html
The dialog route created by this method is pushed to the root navigator. If the application has multiple Navigator objects, it may be necessary to call Navigator.of(context, rootNavigator: true).pop(result) to close the dialog rather than just Navigator.pop(context, result).
So any one of the below should work for you
Navigator.of(context, rootNavigator: true).pop(result)
Navigator.pop(context, result)
You don't need to close and reopen the dialog. Instead let flutter handle the dialog update. The framework is optimised for just that.
Here is a working example app that you can use as a starting point (just add your own Dialogs class):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyApp',
home: Login(
child: Home(),
),
);
}
}
class Home extends StatefulWidget {
final Dialogs dialog = Dialogs();
#override
State<StatefulWidget> createState() => HomeState();
}
class HomeState extends State<Home> {
#override
void didChangeDependencies() {
super.didChangeDependencies();
Future.delayed(Duration(milliseconds: 50)).then((_) {
widget.dialog.loginLoading(
context,
LoginStateProvider.of(context).type,
LoginStateProvider.of(context).description,
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Updating Dialog'),
),
body: Container(),
);
}
}
class Login extends StatefulWidget {
final Widget child;
Login({#required this.child});
#override
State<StatefulWidget> createState() => LoginState();
}
class LoginState extends State<Login> {
String type = 'wait';
String description = 'foo';
#override
void didChangeDependencies() {
super.didChangeDependencies();
Future.delayed(Duration(milliseconds: 2000)).then((_) {
setState(() {
type = 'error';
description = 'bar';
});
});
}
#override
Widget build(BuildContext context) {
return LoginStateProvider(widget.child, type, description);
}
}
class LoginStateProvider extends InheritedWidget {
final String type;
final String description;
LoginStateProvider(Widget child, this.type, this.description)
: super(child: child);
#override
bool updateShouldNotify(LoginStateProvider old) {
return type != old.type || description != old.description;
}
static LoginStateProvider of(BuildContext context) =>
context.inheritFromWidgetOfExactType(LoginStateProvider);
}

Resources