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
],
),
);
}
}
Related
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'm trying to build a Notes app with backup and restore functionality. I have a home page that shows up when the app is opened. This page has a Scaffold as it's body, which in turn has a drawer that has ListTiles for backup and restore. I use a HomeBloc object to interact with the database where I save the notes, hence I used Provider to get access to it everywhere.
The ListTiles open a MaterialPageRoute to new screens where the user is prompted to choose the file, enter passwords etc.
When I tap on the Restore ListTile in the drawer, I get this error:
The following ProviderNotFoundException was thrown building RestoreLocalBackupPage(dirty, state: _RestoreLocalBackupPageState#4f937):
Error: Could not find the correct Provider<HomeBloc> above this RestoreLocalBackupPage Widget
This likely happens because you used a `BuildContext` that does not include the provider
of your choice.
This is my main.dart, where I wrap the Home page in a Provider:
void main() {
runApp(
MyApp()
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Notes',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Provider(
create: (_) => HomeBloc(),
child: HomePage(),
)
);
}
}
This is the build method of my HomePage:
Widget build(BuildContext context) {
homeBloc = Provider.of<HomeBloc>(context);
return Scaffold(
backgroundColor: Color.fromRGBO(219, 243, 250, 1),
appBar: AppBar(...),
body: StreamBuilder<List<Note>>(...),
floatingActionButton: FloatingActionButton(...),
drawer: HomeDrawer(),
);
}
The HomeDrawer's build method returns a Drawer, which has a ListView as it's child. Here's the code for the ListTile that launches the Restore Backup page:
ListTile(
title: Text('Local Backup',
style: GoogleFonts.sourceSansPro(
textStyle: TextStyle(fontWeight: FontWeight.w500),
fontSize: 16)),
onTap: () async {
// Update the state of the app
// ...
// Then close the drawer
bool permissionGranted = await _getPermissions(context);
if (permissionGranted) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => CreateLocalBackupPage(
currentBackupDirectory: currentBackupDirectory
),
)
);
}
},
)
This is the error that I get when I tap on the above ListTile:
The following ProviderNotFoundException was thrown building RestoreLocalBackupPage(dirty, state: _RestoreLocalBackupPageState#4f937):
Error: Could not find the correct Provider<HomeBloc> above this RestoreLocalBackupPage Widget
This likely happens because you used a `BuildContext` that does not include the provider
of your choice.
HomeDrawer()'s BuildContext does have access to the HomeBloc object I need. Hence, wrapping the RestoreLocalBackupPage widget inside another Provider works:
HomeBloc homebloc = Provider.of<HomeBloc>(context);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Provider(
create: (_) => homebloc,
child: RestoreLocalBackupPage(currentBackupDirectory: currentBackupDirectory),
)
)
);
I wanted to know if there's a simpler, more elegant way of getting access to HomeBloc inside RestoreLocalBackupPage using Provider. Dependency Injection via the constructor works but that sort of defeats the purpose of using Provider in the first place, right?
Wrapping the MaterialApp in main.dart with a Provider solved my issue. I have found the solution here. Check rrousselGit's answer.
After doing that, main.dart now becomes:
void main() {
runApp(
MyApp()
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
LicenseRegistry.addLicense(() async* {
final license = await rootBundle.loadString('google_fonts/Cabin_OFL.txt');
yield LicenseEntryWithLineBreaks(['google_fonts_Cabin'], license);
});
LicenseRegistry.addLicense(() async* {
final license = await rootBundle.loadString('google_fonts/SSP_OFL.txt');
yield LicenseEntryWithLineBreaks(['google_fonts_SSP'], license);
});
return Provider(
create: (_) => HomeBloc(),
child: MaterialApp(
title: 'Notes',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
),
);
}
}
Try to use provider one level up and wrap MaterialApp with Provider.
I have a app class that returns a MaterialApp() which has it's home set to TheSplashPage(). This app listens to the preferences notifier if any preferences are changed.
Then in TheSplashPage() I wait for some conditionals to be true and if they are I show them my nested material app.
Side Note: I use a material app here because it seems more logical since it has routes that the parent material app shouldn't have. And also once the user is unauthenticated or gets disconnected I want the entire nested app to shut down and show another page. This works great!
But my problem is the following. Both apps listen to ThePreferencesProvider() so when the theme changes they both get notified and rebuild. But this is a problem because whenever the parent material app rebuilds, it returns the splash page. So now I am back on TheSplashPage() whenever I change a setting on TheSettingsPage().
So my question is how can I stop my application from going back to the TheSplashPage() whenever I change a setting?
Main.dart
void main() {
runApp(App());
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return MultiProvider(
providers: [
ChangeNotifierProvider<PreferencesProvider>(create: (_) => PreferencesProvider()),
ChangeNotifierProvider<ConnectionProvider>(
create: (_) => ConnectionProvider(),
),
ChangeNotifierProvider<AuthenticationProvider>(create: (_) => AuthenticationProvider()),
],
child: Consumer<PreferencesProvider>(builder: (context, preferences, _) {
return MaterialApp(
home: TheSplashPage(),
theme: preferences.isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
debugShowCheckedModeBanner: false,
);
}),
);
}
}
TheSplashPage.dart
class TheSplashPage extends StatelessWidget {
static const int fakeDelayInSeconds = 2;
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.delayed(new Duration(seconds: fakeDelayInSeconds)),
builder: (context, delaySnapshot) {
return Consumer<ConnectionProvider>(
builder: (BuildContext context, ConnectionProvider connectionProvider, _) {
if (delaySnapshot.connectionState != ConnectionState.done ||
connectionProvider.state == ConnectionStatus.uninitialized) return _buildTheSplashPage(context);
if (connectionProvider.state == ConnectionStatus.none) return TheDisconnectedPage();
return Consumer<AuthenticationProvider>(
builder: (BuildContext context, AuthenticationProvider authenticationProvider, _) {
switch (authenticationProvider.status) {
case AuthenticationStatus.unauthenticated:
return TheRegisterPage();
case AuthenticationStatus.authenticating:
return TheLoadingPage();
case AuthenticationStatus.authenticated:
return MultiProvider(
providers: [
Provider<DatabaseProvider>(create: (_) => DatabaseProvider()),
],
child: Consumer<PreferencesProvider>(
builder: (context, preferences, _) => MaterialApp(
home: TheGroupManagementPage(),
routes: <String, WidgetBuilder>{
TheGroupManagementPage.routeName: (BuildContext context) => TheGroupManagementPage(),
TheGroupCreationPage.routeName: (BuildContext context) => TheGroupCreationPage(),
TheGroupPage.routeName: (BuildContext context) => TheGroupPage(),
TheSettingsPage.routeName: (BuildContext context) => TheSettingsPage(),
TheProfilePage.routeName: (BuildContext context) => TheProfilePage(),
TheContactsPage.routeName: (BuildContext context) => TheContactsPage(),
},
theme: preferences.isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
debugShowCheckedModeBanner: false,
)),
);
}
});
});
});
}
TheSettingsPage.dart
Switch(
value: preferences.isDarkMode,
onChanged: (isDarkmode) => preferences.isDarkMode = isDarkmode,
),
You fell for the XY problem
The real problem here is not "my widget rebuilds too often", but "when my widget rebuild, my app returns to the splash page".
The solution is not to prevent rebuilds, but instead to change your build method such that it fixes the issue, which is something that I detailed previously here: How to deal with unwanted widget build?
You fell for the same issue as in the cross-linked question: You mis-used FutureBuilder.
DON'T:
#override
Widget build(BuildContext context) {
return FutureBuilder(
// BAD: will recreate the future when the widget rebuild
future: Future.delayed(new Duration(seconds: fakeDelayInSeconds)),
...
);
}
DO:
class Example extends StatefulWidget {
#override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
// Cache the future in a StatefulWidget so that it is created only once
final fakeDelayInSeconds = Future<void>.delayed(const Duration(seconds: 2));
#override
Widget build(BuildContext context) {
return FutureBuilder(
// Rebuilding the widget no longer recreates the future
future: fakeDelayInSeconds,
...
);
}
}
When using Consumer, you are forcing the widget to rebuild every time you notify listeners.
To avoid such behaviour, you can use Provider.of as stated in ian villamia's answer, as it can be used wherever you need it, and only where you need it.
The changes in your code to use Provider.of would be removing the consumer and adding Provider.of when resolving the theme as follows:
theme: Provider.of<PreferencesProvider>(context).isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
HOWEVER if you want to keep using Consumer, you can do something else:
The child property on the Consumer widget is a child that is not rebuilt. You can use this to set the TheSpashScreen there, and pass it to the materialApp through the builder.
TL:DR
Use Provider.of if you need only to tap into one variable for simplicity.
Use Consumer with its child property as the child doesn't rebuild. <= Better performance
Using Provider.of
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return MultiProvider(
providers: [
ChangeNotifierProvider<PreferencesProvider>(create: (_) => PreferencesProvider()),
ChangeNotifierProvider<ConnectionProvider>(
create: (_) => ConnectionProvider(),
),
ChangeNotifierProvider<AuthenticationProvider>(create: (_) => AuthenticationProvider()),
],
child: Builder(
builder: (ctx) {
return MaterialApp(
home: TheSpashPage(),
theme: Provider.of<PreferencesProvider>(ctx).isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
);
}),
);
}
}
Using Consumer
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return MultiProvider(
providers: [
ChangeNotifierProvider<PreferencesProvider>(create: (_) => PreferencesProvider()),
ChangeNotifierProvider<ConnectionProvider>(
create: (_) => ConnectionProvider(),
),
ChangeNotifierProvider<AuthenticationProvider>(create: (_) => AuthenticationProvider()),
],
child: Consumer<PreferencesProvider>(
child: TheSpashPage(),
builder: (context, preferences, child) {
return MaterialApp(
home: child,
theme: preferences.isDarkMode ? DarkTheme.themeData : LightTheme.themeData,
debugShowCheckedModeBanner: false,
);
}),
);
}
}
I hope this is helpful for you!
basically there's 2 ways in using a provider
one it the current one you're using which is the consumer type,
is using the instance of a provider
final _preferencesProvider= Provider.of<PreferencesProvider>(context, listen: false);
you can toggle the "listen:true" if you want the widget to rebuild when notifyListeners() are called... false if otherwise
also just use _preferencesProvider.someValue like any other instance
I'm new to Flutter and provider package so any assistance would be great, so the issue I have my main.dart file which is as follows:
void main() => runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
home: ChangeNotifierProvider(
create: (_) => InterestingMomentProvider(),
child: Home(),
),
),
);
This builds my Home widget, I won't post it all as It's extremely large, however, what happens is I click a button and it passes in a string to the provider class an adds it to the list which is outlined as follows:
import 'package:flutter/widgets.dart';
class InterestingMomentProvider extends ChangeNotifier {
List<String> _moments = [];
List<String> get moments => _moments;
void addMoment(String time){
_moments.add(time);
}
int momentsTotal(){
return _moments.length;
}
}
Adding a breakpoint on the addMoment method I can confirm _moments has all the strings.
I then press a button which navigates to another screen, the navigation code is as follows:
Navigator.push(context, MaterialPageRoute(builder: (context) => MomentsRecorded()),);
MomentsRecorded widget is as follows:
class MomentsRecorded extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Moments'),
centerTitle: true,
),
body: Center(
child: MomentsList()
),
);
}
}
the first error was:
Could not find the correct Provider<InterestingMomentProvider> above this Consumer<InterestingMomentProvider> Widget
To fix, please:
* Ensure the Provider<InterestingMomentProvider> is an ancestor to this Consumer<InterestingMomentProvider> Widget
* Provide types to Provider<InterestingMomentProvider>
* Provide types to Consumer<InterestingMomentProvider>
* Provide types to Provider.of<InterestingMomentProvider>()
* Always use package imports. Ex: `import 'package:my_app/my_code.dart';
* Ensure the correct `context` is being used.
I then tweaked the body to look like the following and the error dissappeared:
body: Center(
child: ChangeNotifierProvider(
create: (_) => InterestingMomentProvider(),
child: MomentsList())
),
However inside MomentLists widget, I try to loop through the list of moments from the provider class, however when debugging _moments is 0 ?
MomentsList widget:
class MomentsList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<InterestingMomentProvider>(
builder: (context, momentData, child){
return momentData.momentsTotal() > 0 ? ListView.builder(
itemCount: momentData.momentsTotal(),
itemBuilder: (BuildContext context, int index) {
final moment = momentData.moments[index];
return ListTile(
title: Text(moment),
);
}
) : Center(child: Text('no moments recorded'),);
}
);
}
}
Can someone please explain why this maybe?
This is happening, because your provider is defined in home property of MaterialApp, so when you change the route the provider will be removed too.
Solution: move the provider above the MaterialApp like this:
void main() => runApp(
ChangeNotifierProvider(
create: (_) => InterestingMomentProvider(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
home: Home()
),
),
);
If you are afraid that this isn't right - checkout the docs, they are doing the same