flutter MultiProvider Consumer - flutter

I use providers in my app, below code is working but doesnt look right :)
Looking for some help how reformat the code to only use child once..
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AppProvider(),
),
ChangeNotifierProvider(
create: (_) => ThemeProvider(true),
),
],
child: Consumer<AppProvider>(
builder: (ctx, providerApp, _) => Container(
child: Consumer<ThemeProvider>(
builder: (ctx, providerTheme, _) => MaterialApp(
debugShowCheckedModeBanner: false,
theme: providerTheme.getTheme(),
home: Splash(),
),
),
),
),
);
}
}

Related

I am trying to navigate home screen via Start screen through main.dart but failed to navigate. I am new to flutter and can't understand this error. Pl

This is my main .dart and the error is here that is In This Image this is the error that I got on the screen. I am trying to solve but it is not in my hand to solve, Swipe Bloc throws an error, I tried too much to solve it but every time I see failure to solve it.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// runApp(MyApp());
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: MyLogin(),
routes: {
'register': (context) => MyRegister(),
'login': (context) => MyLogin(),
},
)
);
}
Here is the stateless widget which throws the particular error which I don't know where it throws.
class MyHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiRepositoryProvider(
providers: [
RepositoryProvider(
create: (context) => AuthRepository(),
),
RepositoryProvider(
create: (context) => StorageRepository(),
),
RepositoryProvider(
create: (context) => DatabaseRepository(),
),
],
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => AuthBloc(
authRepository: context.read<AuthRepository>(),
),
),
// BlocProvider(
// create: (context) => SwipeBloc()
// ..add(
// LoadUsers(users: User.users.where((user) => user.id != 1).toList(),
// ),
// ),
// ),
BlocProvider<SignupCubit>(
create: (context) =>
SignupCubit(authRepository: context.read<AuthRepository>()),
),
BlocProvider<OnboardingBloc>(
create: (context) => OnboardingBloc(
databaseRepository: context.read<DatabaseRepository>(),
storageRepository: context.read<StorageRepository>(),
),
),
BlocProvider(
create: (context) => ProfileBloc(
authBloc: context.read<AuthBloc>(),
databaseRepository: context.read<DatabaseRepository>(),
)..add(
LoadProfile(userId: context.read<AuthBloc>().state.user!.uid),
),
),
],
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => SwipeBloc()
..add(
LoadUsers(users: User.users.where((user) => user.id != 1).toList(),
),
),
),
],
child:
MaterialApp(
),
),
),
);
}
}
This error I got on my android sceen.
Try to initialize all your providers above MaterialApp, like in this code
void main() {
runApp(
MultiRepositoryProvider(
providers: [
RepositoryProvider(
create: (context) => AuthRepository(),
),
RepositoryProvider(
create: (context) => StorageRepository(),
),
RepositoryProvider(
create: (context) => DatabaseRepository(),
),
],
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => AuthBloc(
authRepository: context.read<AuthRepository>(),
),
),
],
child: MaterialApp(),
),
);
}

MultiProvider in main instead of inside the tree

Does inserting all providers in main affect performance? Is it a better option to put individual providers in the middle of the tree where they are needed?
When injecting many values in big applications, Provider can rapidly become pretty nested:
Provider<Something>(
create: (_) => Something(),
child: Provider<SomethingElse>(
create: (_) => SomethingElse(),
child: Provider<AnotherThing>(
create: (_) => AnotherThing(),
child: someWidget,
),
),
),
To:
MultiProvider(
providers: [
Provider<Something>(create: (_) => Something()),
Provider<SomethingElse>(create: (_) => SomethingElse()),
Provider<AnotherThing>(create: (_) => AnotherThing()),
],
child: someWidget,
)
**
The behavior of both examples is strictly the same. MultiProvider only changes the appearance of the code.
**
Try importing 'package:provider/provider.dart' as statemanagement; and use multi provider as statemanagement.MultiProvider(...), and use Provider as statemanagement.Provider(....) worked on my code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart' as statemanagement;
import 'models/model_provider.dart';
import 'modules/screen_root.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return statemanagement.MultiProvider(
providers: [
statemanagement.Provider<ModelProvider>(
create: ((_) => ModelProvider())),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Multi Provider',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const ScreenRoot(),
),
);
}
}

Flutter 'No Overlay widget exists above EditableText' error when using Provider

Hey I've got a MultiProvider setup, and it's now throwing this error when tapping on a TextField whereas it didn't before I implemented the multi-provider:
No Overlay widget exists above EditableText
The error text does not give any helpful indication of what in the code is causing the issue, here is the code:
import 'package:***_mobile/Providers/user_auth_provider.dart';
import 'package:***_mobile/screens/browsing_page.dart';
import 'package:***_mobile/screens/film_details_page.dart';
import 'package:***_mobile/screens/login_screen.dart';
import 'package:***_mobile/screens/venue_details_page.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(***());
}
class *** extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => UserRepository.instance(),
child: Consumer(
builder: (context, UserRepository user, _) {
return MaterialApp(
title: 'Flutter Demo',
routes: {
'/browsing': (ctx) => BrowsingPage(),
'/venueDetails': (ctx) => VenueDetailPage(),
'/filmDetails': (ctx) => FilmDetailPage(),
'/login': (ctx) => LoginPage()
},
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
builder: (ctx, _) {
switch (user.status) {
case Status.Uninitialized:
return LoginPage();
case Status.Unauthenticated:
return LoginPage();
case Status.Authenticating:
return LoginPage();
case Status.Authenticated:
return BrowsingPage();
default:
return LoginPage();
}
},
);
},
),
);
}
}
Excuse for my language, i hope you will understand, what i was wrote.
You need wrap every widget, which you return from builder in Navigator
Navigator(
onGenerateRoute: (_) => MaterialPageRoute(
builder: (ctx) => Scaffold(
body: Stack(
children: [
Positioned.fill(
child: buildBody(
context.watch<AuthScopeViewModel>().screenType))
],
),
),
),
);

Flutter - losing Provider state on hot reload

When I hot-reload my app, I lose state from my Provider classes. I'm aware that Provider properly preserves state so I know the issue comes from my code, but I cannot figure out where I'm going wrong. I have the following:
main.dart
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: GlobalData.fetchTheme(),
builder: (BuildContext context, AsyncSnapshot<String> themeSnapshot) {
if (themeSnapshot.hasData) {
GlobalData.theme = themeSnapshot.data;
return Phoenix(
child: MaterialApp(
title: 'MyApp',
home: Scaffold(body: Root()),
theme: ThemeData(
accentColor: Palette.green
),
)
);
}
return Loading();
}
);
}
}
root.dart
class Root extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUser>(
stream: FireAuth().getInstance().onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
FirebaseUser user = snapshot.data;
if (user != null) {
GlobalData.setUid(user.uid);
}
return user == null ? Auth() : SafeArea(child: KeyboardDismisser(child: Core()));
} else {
return Loading();
}
},
);
}
}
core.dart
class Core extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: <Widget>[
FutureBuilder(
future: Users.getUser(GlobalData.uid),
builder: (BuildContext context, AsyncSnapshot<TuneUser> userSnapshot) {
if (userSnapshot.hasData) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserProvider(userSnapshot.data)),
ChangeNotifierProvider(create: (context) => SearchProvider()),
ChangeNotifierProvider(create: (context) => DetailsProvider()),
],
child: Expanded(child: BottomBarNavigation())
);
}
return Expanded(child: Loading());
}
),
BottomBar()
]
);
}
}
I assume at some point I'm rebuilding or losing state when I shouldn't, I've tried a lot of things with the help of similar questions already present, but I can't seem to fix it.
Any ideas?
Would really appreciate the help.
Cheers,
Andy
wrap you MultiProvider in the main.dart
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserProvider(userSnapshot.data)),
ChangeNotifierProvider(create: (context) => SearchProvider()),
ChangeNotifierProvider(create: (context) => DetailsProvider()),
],
child: child: MaterialApp(
title: 'MyApp',
home: Scaffold(body: Root()),
theme: ThemeData(
accentColor: Palette.green
),
),
),

Flutter problem with finding provider context

I have a problem with Flutter Provider pattern. After user is redirected to a new screen, the provider could not be found.
Following my previous question (Could not find the correct provider above this widget)
I wrote this code:
class NewRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
final title = 'Tap to select';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: NewRouteBody()
));
}
}
class NewRouteBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
var user = Provider.of<UserRepository>(context);
return ListView(...)
I did same thing but I get again the error which says that it could not find the correct provider above this widget (NewRouteBody).
Tried to fix it somehow, Googled the answer for a few hours but without success...
Any help is appreciated.
EDIT
This is UserRepository which contains pattern:
class UserRepository with ChangeNotifier {
User user;
Status _status = Status.Uninitialized;
Status get status => _status;
User get getUser => user;
...}
EDIT 2:
Code snippet with ChangeNotifier:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<UserRepository>(
builder: (context) => UserRepository.instance(),
child: Consumer<UserRepository>(
builder: (context, UserRepository userRepository, _) {
switch (userRepository.status) {
case Status.Uninitialized:
return Login();
case Status.Unauthenticated:
return Login();
case Status.Authenticating:
case Status.Authenticated:
if(userRepository.getUser.isPrefSet == 0){
return Selection();
}
return Dashboard();
}
},
),
);
}
}
The issue is:
Your ChangeNotifierProvider is located inside Home, but you are trying to access it outside Home.
Providers are scoped. Which means that if it's located inside a widget tree, only its descendants can access it. As such, in your code, only Home can read from the provider.
To fix that, move the provider above MaterialApp:
ChangeNotifierProvider<UserRepository> (
builder: (context) => UserRepository(),
child: MaterialApp(
home: Home(),
),
)
You first need to create the Provider and place in the tree above the usage.
for example, in your case:
Widget build(BuildContext context) {
final title = 'Tap to select';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Provider<UserRepository> (
builder: (context) => UserRepository(),
dispose: (context, val) => val.dispose(),
child: NewRouteBody())
));
}
When the application reports such an error, it can be from many reasons. In my case, I was trying to read data from a context that was not wrapped by the BlocProvider from its ancestor.
// In my Child Widget
Navigator.push(context, MaterialPageRoute(
builder: (_) => MultiBlocProvider(providers: [
BlocProvider.value(
value: SaveJobsCubit()),
BlocProvider.value(
value: context.read<OnlineCompaniesCubit>()),
BlocProvider.value(
value: context.read<ApplyJobsCubit>()),
],
child: AttractiveJobsScreen(),
)
// But in Parent Widget, I create MultiBlocProvider with case have access_token
AuthRepo.accessToken != null
? RepositoryProvider(
create: (context) => OnlineCompaniesRepo(),
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => SaveJobsCubit(),
),
BlocProvider(
create: (context) => OnlineCompaniesCubit(context.read<OnlineCompaniesRepo>()),
),
BlocProvider(
lazy: false,
create: (context) => ApplyJobsCubit(),
),
],
child: current,
),
)
: RepositoryProvider(
create: (context) => OnlineCompaniesRepo(),
child: BlocProvider(
create: (context) => OnlineCompaniesCubit(context.read<OnlineCompaniesRepo>()),
child: current,
),
);
This causes an error in case there is no access_token, then the child screen will not have SaveJobsCubit and cause the above error.
Hope this helps someone.