I have my main() like this with MultiProvider wrapped with LocalizedApp for localization:
void main() async {
setupLocator();
var delegate = await LocalizationDelegate.create(
fallbackLocale: 'fa',
supportedLocales: ['fa'],
);
FluroRouter.setupRouter();
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(debug: true);
runApp(
LocalizedApp(
delegate,
MultiProvider(
providers: [
StreamProvider<ConnectionStatus>(
create: (context) =>
ConnectivityService().connectivityController.stream,
initialData: ConnectionStatus.offline,
),
ChangeNotifierProvider<AppState>(
create: (BuildContext context) => AppState(),
),
],
child: MyApp(),
),
),
);
}
and MyApp class is as follows again wrapped with LocalizationProvider:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appstate = Provider.of<AppState>(context);
var localizationDelegate = LocalizedApp.of(context).delegate;
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: appstate.statusBarColor,
));
return LocalizationProvider(
state: LocalizationProvider.of(context).state,
child: GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
localizationDelegate
],
supportedLocales: localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale,
theme: appstate.currentTheme,
initialRoute: 'landing',
onGenerateRoute: FluroRouter.router.generator,
),
),
);
}
}
but even in the initial route which is 'landing' when I try to use a Provider.of<AppState>(context) it throws this error:
Error: Could not find the correct Provider<AppState> above this Landing Widget
This likely happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that Landing is under your MultiProvider/Provider<AppState>.
This usually happen when you are creating a provider and trying to read it immediatly.
For example, instead of:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
consider using `builder` like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builer: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
I don't know what I'm doing wrong here!
also I'm using Fluro v.1.5.1 for navigation.
Related
I'm working on a project and I've been asked to use cubit for internationalization, preferably using the lazy method. For that I have a LocalizationContainer as follows:
class LocalizationContainer extends BlocContainer {
final Widget child;
LocalizationContainer({required this.child});
#override
Widget build(BuildContext context) {
return BlocProvider<CurrentLocaleCubit>(
create: (context) => CurrentLocaleCubit(),
child: child,
);
}
}
class CurrentLocaleCubit extends Cubit<String> {
CurrentLocaleCubit() : super("pt-br");
CurrentLocaleCubit() : super("en-us");
}
In my main file I have the following:
MaterialApp(
title: 'Example',
theme: exampleTheme(context),
debugShowCheckedModeBanner: false,
home: LocalizationContainer(
child: InitialScreenContainer(),
),
);
In this example the child of LocalizationContainer is another container representing the screen. Each screen is structured into container, cubit and view:
The container for screen have the following structure:
class ExampleScreenContainer extends BlocContainer {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ExampleScreenCubit(),
child: I18NLoadingContainer(
language: BlocProvider.of<CurrentLocaleCubit>(context).state,
viewKey : "Example",
creator: (messages) => ExampleScreenView(ExampleScreenViewLazyI18N(messages)),
),
);
}
}
Everytime a new page needs to be opened, I do the following:
Navigator.of(blocContext).push(
MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: BlocProvider.of<CurrentLocaleCubit>(blocContext),
child: NewScreenContainer(),
),
),
);
But whenever I try to hot reload a error pops up. It only works if I do the hot restart. Does somebody know how to solve this problem, or this internationalization method is wrong?
I did not really get the problem (I think if you put the error that's pop up I can help you more), but this way I do localizations (I use bloc).
first off all you need to add BlocProvider above MaterialApp so he become ancestor to every widget in context tree, so when ever you called BlocProvider.of(context)
you can get the instance of this bloc where ever you are in the tree (no need to do BlocProvider above every screen you are pushing).
now when ever you change language of your app and yield the new state the BlocBuilder will rebuild the whole app with the new language.
class AppProvider extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiBlocProvider(providers: [
BlocProvider<AppBloc>(
create: (_) => sl<AppBloc>()
//get app default language
..add(const AppEvent.initialEvent()),
),
], child: App());
}
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<AppBloc, AppState>(
builder: (context, state) => MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen()),
locale: state.language == AppLanguageKeys.AR
? const Locale('ar', '')
: const Locale('en', ''),
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''), // English
const Locale('ar', ''), // Arabic
],
),
);
}
}
I am trying to write widget tests for my Flutter application.
I have a page (MainMenuScreen) as follows (I've left out the imports):
class MainMenuScreen extends StatefulWidget {
#override
_MainMenuScreenState createState() => _MainMenuScreenState();
}
class _MainMenuScreenState extends State<MainMenuScreen> {
#override
void initState() {
super.initState();
}
late MainMenuViewModel vm;
#override
Widget build(BuildContext context) {
vm = Provider.of<MainMenuViewModel>(context);
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Center(
child: Column(
children: <Widget>[
generateHeightSpacer(Dimensions().heightSpace),
generateLogo(Dimensions().fractionalWidth),
generateHeightSpacer(Dimensions().fractionalWidth),
generateMenuButton(
context,
Dimensions().fractionalWidth,
Dimensions().fractionalHeight,
AppLocalizations.of(context).menuPlay,
LevelSelectionScreen()),
generateHeightSpacer(Dimensions().heightSpace),
generateMenuButton(
context,
Dimensions().fractionalWidth,
Dimensions().fractionalHeight,
AppLocalizations.of(context).menuAbout,
AboutScreen()),
],
),
)),
);
}
}
My main.dart file looks like this:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => MainMenuViewModel()),
ChangeNotifierProvider(create: (context) => LevelSelectionViewModel()),
ChangeNotifierProvider(create: (context) => LevelViewModel()),
],
child: MaterialApp(
onGenerateTitle: (context) {
return AppLocalizations.of(context).appTitle;
},
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('en', ''), // English, no country code
Locale('es', ''), // Spanish, no country code
],
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChangeNotifierProvider(
create: (context) => MainMenuViewModel(),
child: MainMenuScreen(),
// ),
),
),
);
}
}
I have looked into how to write a test that can identify when the About screen is displayed, i.e. after pressing the last Menu Button.
So, for now I have something as follows:
testWidgets("Flutter Main Menu Test Menu Button About",
(WidgetTester tester) async {
await tester.pumpWidget(MyApp());
var button = find.text("About");
expect(button, findsOneWidget);
});
This allows me to identify that the About button is displayed. (Note: I will be changing my code and tests to do this based on a 'key' rather than text.)
From what I've read the way to test is to use Mockito and a NavigationObserver but that seems to require the ability to inject into the screen. I have also been looking at other solutions which seem to suggest wrapping the provider, i.e.:
I've been following this page(https://iiro.dev/writing-widget-tests-for-navigation-events/) to try to test but I can't quite figure out how to pass the viewmodel in.
This is my test at the moment:
class MockMainMenuViewModel extends Mock implements MainMenuViewModel {}
class MockNavigatorObserver extends Mock implements NavigatorObserver {}
void main() {
group('MainMenuScreen navigation tests', () {
late NavigatorObserver mockObserver;
MockMainMenuViewModel? mockViewModel;
setUp(() {
mockObserver = MockNavigatorObserver();
mockViewModel = MockMainMenuViewModel();
});
Future<void> _buildMainPage(WidgetTester tester) {
return tester.pumpWidget(MaterialApp(
home: MainMenuScreen(mockViewModel),
navigatorObservers: <NavigatorObserver>[observer],
));
}
});
}
Any help is appreciated please.
it has a simple solution but i =cant find it . I have tried some solutions on github and stack overflow but it didnt work.
lets take a look at my code :
here is my brewlist.dart :
class BrewList extends StatelessWidget {
#override
Widget build(BuildContext context) {
final brew = Provider.of<List<Brew>>(context);
brew.forEach((brew) {
print(brew.name);
print(brew.strength);
print(brew.sugars);
});
return Container();
}
}
its above widget is here in the home.dart file
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
final AuthService _authService = AuthService();
return StreamProvider<List<Brew>?>.value(
initialData: null,
value: DataBase('fjsbfjjhsbdfgvbdkbvkj').brews,
child: Scaffold(
backgroundColor: Colors.brown[50],
appBar: AppBar(
backgroundColor: Colors.brown[400],
elevation: 12,
title: Text('Brew Crew'),
actions: <Widget>[
TextButton.icon(
onPressed: () async {
await _authService.signOut();
},
icon: Icon(
Icons.person,
color: Colors.brown[800],
),
label: Text(
'log out',
style: TextStyle(color: Colors.brown[800]),
),
),
],
),
body: BrewList(),
),
);
}
}
brewlist is the body of my scaffold as you can see we have the StreamProvider wrapped around the scaffold to have the data passing through its childs {which brewlist is one of them ..
here is my main.dart file :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<ToggleView>(create: (_) => ToggleView()),
ChangeNotifierProvider<LoadingIndicator>(
create: (_) => LoadingIndicator()),
],
child: MyApp(),
),
);
}
enter code here
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return StreamProvider<Userr?>.value(
value: AuthService().user,
initialData: Userr(useruid: 'No user found'),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Wrapper(),
),
);
}
}
i really dont understand this long error :
The following ProviderNotFoundException was thrown building BrewList(dirty):
Error: Could not find the correct Provider<List> above this BrewList Widget
This happens because you used a BuildContext that does not include the provider
of your choice. There are a few common scenarios:
You added a new provider in your main.dart and performed a hot-reload.
To fix, perform a hot-restart.
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
You used a BuildContext that is an ancestor of the provider you are trying to read.
Make sure that BrewList is under your MultiProvider/Provider<List>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) {
return Provider(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because context is associated
// to the widget that is the parent of Provider<Example>
child: Text(context.watch()),
),
}
consider using builder like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
If none of these solutions work, consider asking for help on StackOverflow:
i dont why I get this error if you can provide solution please help
i really appreciate your help inadvacne.
The solution is very simple, my friend.
Just go to brew_list.dart file and add the following line
final brews = Provider.of<List<Brew?>?>(context) ?? [];
That's it, you are good to go :)
I am working on Food delivery app and I am using Provider as state management architecture. Problem is when i add a second provider to my app it is giving error.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider<GPSViewModel>(create: (_) => GPSViewModel()),
ChangeNotifierProvider<OTPViewModel>(create: (_) => OTPViewModel()),
],
child: GPS(),
),
);
}
Error is
Error: Could not find the correct Provider<OTPViewModel> above this MobileOTP Widget
In MobileOTP i am accessing the provider like this in init state method
#override
void initState() {
super.initState();
Provider.of<OTPViewModel>(context, listen: false).
verifyMobileNumber(widget.phone,verificationCompleted,verificationFailed,codeSent,codeAutoRetrievalTimeout);
}
The Full error trace is like this
Error: Could not find the correct Provider<OTPViewModel> above this MobileOTP Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that MobileOTP is under your MultiProvider/Provider<OTPViewModel>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
```
consider using `builder` like so:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
What i am doing wrong ?
So basically problem was "Provider is based on InheritedWidget. Only child widgets can inherit parent widget's state.". I was trying to access it otherwise, so it was giving me error. I swap the Material App with Multi provider and it fixes the problem.
Code now becomes
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<GPSViewModel>(create: (_) => GPSViewModel()),
ChangeNotifierProvider<OTPViewModel>(create: (context) => OTPViewModel()),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: GPS(),
),
);
}
Thats it !!!
Do not ignore the context, use it while you define them, like this:
MultiProvider(
providers: [
ChangeNotifierProvider<GPSViewModel>(create: (ctx) => GPSViewModel()),
ChangeNotifierProvider<OTPViewModel>(create: (ctx) => OTPViewModel()),
],
I'm New in this FrameWork and here initial Rout is not Accepting the Loggin Session value Please help me with this. I tried to add Home with the splash screen but that also not working I'm not getting What's wrong in this.
This is my main Page
Future main() async {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
));
runApp(Phoenix(child: AmericanCuisine()));
}
class AmericanCuisine extends StatefulWidget {
#override
_AmericanCuisineState createState() => _AmericanCuisineState();
}
class _AmericanCuisineState extends State<AmericanCuisine> {
bool isLoggedIn;
#override
void initState() {
super.initState();
getData();
}
getData() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences storage = await SharedPreferences.getInstance();
setState(() {
isLoggedIn = storage.getBool("loggedIn");
});
}
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<LanguageCubit>(
create: (context) => LanguageCubit(),
),
BlocProvider<ThemeCubit>(
create: (context) => ThemeCubit(),
),
],
in this page after using BlockBuilder how i To give the Initial Route
child: BlocBuilder<ThemeCubit, ThemeData>(
builder: (_, theme) {
return BlocBuilder<LanguageCubit, Locale>(
builder: (_, locale) {
return MaterialApp(
localizationsDelegates: [
const AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
],
locale: locale,
theme: theme,
//This initial rout is not working.
initialRoute: isLoggedIn == false ?'/': '/homeOrderAccount',
routes: {
// When navigating to the "/" route, build the FirstScreen widget.
'/': (context) => OpeningScreen(),
'/homeOrderAccount': (context) => HomeOrderAccount(),
},
);
},
);
},
),
);
}
}
You can't use initialRoute with routes map either delete '/' from the routes map or delete the initialRoute: