I am implementing ThemeMode feature on my fluter app for example if user have saved ThemeMode (light, dark or system) saved in SharedPreferences then it should load on app startup otherwise bydefault it is light. so i am using Provider for state management.
Here is my approach:
on themes_provider.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../utils/themes.dart';
class ThemesProvider extends ChangeNotifier {
ThemeMode? _themeMode;
ThemeMode? get themeMode => _themeMode;
ThemeData get lightTheme => ThemeManager().lightTheme;
ThemeData get darkTheme => ThemeManager().darkTheme;
void initThemeMode() async {
final _prefs = await SharedPreferences.getInstance();
final _currentTheme = _prefs.getString('theme') ?? 'light';
if (_currentTheme == 'light') {
_themeMode = ThemeMode.light;
} else if (_currentTheme == 'dark') {
_themeMode = ThemeMode.dark;
} else {
_themeMode = ThemeMode.system;
}
notifyListeners();
}
void setThemeMode(int value) async {
final _prefs = await SharedPreferences.getInstance();
_prefs.setInt('theme', value);
notifyListeners();
}
}
and then on main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserProvider()),
ChangeNotifierProvider(
create: (context) => ThemesProvider(),
)
],
builder: (context, snapshot) {
final _themeNotifier =
Provider.of<ThemesProvider>(context, listen: false);
_themeNotifier.initThemeMode();
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: _themeNotifier.lightTheme,
darkTheme: _themeNotifier.darkTheme,
themeMode: _themeNotifier.themeMode,
home: HomeScreen(),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: RouteGenerator.loginRoute,
);
});
}
}
But in debug console i am getting null value on _themeNotifier.themeMode
please help.
Please I want to know how to launch WhatsApp in the Flutter webview app or launch WhatsApp from the browser in Flutter, have used many codes with no errors but they do not work.Am using mac m1
and vscode
import 'package:coinpaga/Connectivity_Provider.dart';
import 'package:coinpaga/services/local_notification_service.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'homepage.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:colorful_safe_area/colorful_safe_area.dart';
/// Receive message when app is in background solution for on
message
Future<void> backgroundHandler(RemoteMessage message)async{
print(message.data.toString());
print(message.notification!.title);
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
LocalNotificationServices.initialize();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ConnectivityProvider(),
child: HomePage(),
)
],
child:MaterialApp(
title: 'coinpaga',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: ColorfulSafeArea(
color: HexColor("#2e2a42"),
top: true,
bottom: false,
left: false,
right: false,
child: HomePage(),
)
),
);
}
}
Home.dart
import 'package:coinpaga/Connectivity_Provider.dart';
import 'package:coinpaga/no_internet.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
// ignore: unused_field
final _flutterwebview = FlutterWebviewPlugin();
HomePage({ Key? key }) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
void initState() {
super.initState();
Provider.of<ConnectivityProvider>(context, listen:
false).startMonitoring();
}
#override
Widget build(BuildContext context) {
return pageUI();
}
#override
void dispose() {
_flutterwebview.dispose();
super.dispose();
}
}
Widget pageUI() {
return Consumer<ConnectivityProvider>(
builder: (context, model, child) {
return model.isOnline
? WebviewScaffold(
url: 'https://coinpaga.com',
withLocalStorage: true,
withJavascript: true,
scrollBar: false,
initialChild: Center(child: Text('Loading...')),
) : NoInternet();
},
);
}
// ignore: camel_case_types
class _flutterwebview {
static void dispose() {}
}
Please help me go through it.
String text = "Hello World !! Hey There";
String url = "https://wa.me/?text=${Uri.encodeFull(text)}";
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url),
mode: LaunchMode.externalApplication);
}
First, add url_launcher, then I use this code to launch whatsapp on flutter webview and works.
WebView(
initialUrl: getUrl(_url),
javascriptMode: JavascriptMode.unrestricted,
navigationDelegate: (NavigationRequest request) async {
if (request.url
.startsWith('https://api.whatsapp.com/send?phone')) {
print('blocking navigation to $request}');
List<String> urlSplitted = request.url.split("&text=");
String phone = "0123456789";
String message =
urlSplitted.last.toString().replaceAll("%20", " ");
await _launchURL(
"https://wa.me/$phone/?text=${Uri.parse(message)}");
return NavigationDecision.prevent;
}
print('allowing navigation to $request');
return NavigationDecision.navigate;
},
)
_launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
You can use url_launcher to launch URLs.
You can give https://api.whatsapp.com/send/?phone=(phone_number) URL to launch.
For the launching the WhatsApp Website use launch('https://api.whatsapp.com/send/?phone=(phone_number)')
Make sure you give your country code without (+).
i want wait 5 seconds on main page and display loading animation then navigate to another page.
here is my code
import 'mainPage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'dart:async';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isLoading = true;
#override
void initState() {
super.initState();
loadData();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.cyan,
body: Builder(
builder: (context) => Center(
child: Container(
child: SpinKitCubeGrid(color: Colors.white, size: 50.0),
),
),
),
),
);
}
Future loadData() async {
return new Timer(Duration(seconds: 5), () {
setState(() {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => MainPage()));
});
});
}
}
but i got this error:
Unhandled Exception: Navigator operation requested with a context that does not include a Navigator.
what should i do?
Wrap MyApp with MaterialApp which will provide the right context to Navigator
void main() {
runApp(MaterialApp(home: MyApp()));
}
Maybe this will help U
static Route route() {
return MaterialPageRoute<void>(builder: (_) => MyApp());
}
onPressed: () => Navigator.of(context).push<void>(MainPage.route()),
................
static Route route() {
return MaterialPageRoute<void>(builder: (_) => MainPage());
}
onPressed: () => Navigator.of(context).push<void>(MyApp.route()),
Can you try it like this? I didn't run the code but showing the basic idea. Just pass the context and call it from build function.
class _MyAppState extends State<MyApp> {
bool isLoading = true;
#override
Widget build(BuildContext context) {
loadData(context);
return MaterialApp(
...
);
}
Future loadData(context) async {
...
}
}
You need a context to navigate with the Navigator.
To navigate without using context you can use a package called GetX
Example:
Add "Get" before your MaterialApp, turning it into GetMaterialApp
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
Navigate to a new screen:
Get.to(NextScreen());
I am building an app in Flutter, so far I am using the Internationalization with JSON where the language of the app is based on the language that the user has as default in his phone its working pretty well, but I would like to give the user a chance to change the language without changing phone the system language settings, by only clicking in a button and then the application change the language without going through the settings.
Here is the code:
The Main:
import 'package:flutter/material.dart';
import 'package:flutter_app_darkmode/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) => MaterialApp(
supportedLocales: [
Locale('en', "ZA"),
Locale('pt', "MZ"),
],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode &&
supportedLocale.countryCode == locale.countryCode) {
return supportedLocale;
} else {
if (MyHomePage.local != null) {
for (int i = 0; i < supportedLocales.length; i++) {
if (MyHomePage.local == supportedLocales.elementAt(i)) {
return supportedLocales.elementAt(i);
}}}}}
return supportedLocales.first;
},
home: MyHomePage(),
);}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage> {
getLocale() {
Locale myLocale = Localizations.localeOf(context);
print(myLocale);}
#override
Widget build(BuildContext context) {
getLocale();
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
AppLocalizations.of(context).translate('first_string'),
style: TextStyle(fontSize: 25),
textAlign: TextAlign.center,),
Text(
AppLocalizations.of(context).translate('second_string'),
style: TextStyle(fontSize: 25),
textAlign: TextAlign.center,),
RaisedButton(
child: Text('PT'),
onPressed: () {},
),],),),),);}}
The app_locations class:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AppLocalizations {
final Locale locale;
AppLocalizations(this.locale);
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
Map<String, String> _localizedStrings;
Future<bool> load() async {
String jsonString =
await rootBundle.loadString('lang/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
return true;}
String translate(String key) {
return _localizedStrings[key];}}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
#override
bool isSupported(Locale locale) {
return ['en', 'pt'].contains(locale.languageCode);}
#override
Future<AppLocalizations> load(Locale locale) async {
AppLocalizations localizations = new AppLocalizations(locale);
await localizations.load();
return localizations;}
#override
bool shouldReload(_AppLocalizationsDelegate old) => false;}
You can set the locale property of MaterialApp with your favourite state management method. For example:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
static _MyAppState of(BuildContext context) => context.findAncestorStateOfType<_MyAppState>();
}
class _MyAppState extends State<MyApp> {
Locale _locale;
void setLocale(Locale value) {
setState(() {
_locale = value;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
locale: _locale,
home: Dashboard(),
);
}
}
class Dashboard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [
TextButton(
child: Text("Set locale to German"),
onPressed: () => MyApp.of(context).setLocale(Locale.fromSubtags(languageCode: 'de')),
),
TextButton(
child: Text("Set locale to English"),
onPressed: () => MyApp.of(context).setLocale(Locale.fromSubtags(languageCode: 'en')),
),
],
);
}
}
You have to use Localizations given from Flutter. You have to use custom delegate and JSON files for your supported languages.
I implemented using bloc
Steps to follow,
Create a folder assets/languages/ in the root folder
Create JSON files for your supported languages.
Like: en.json, es.json
Create a key, value pairs for your strings in each file accordingly with their specific language strings
In main.dart create default locale, supportedLocales and localizationsDelegates.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:movie_app/common/constants/languages.dart';
import 'package:movie_app/presentation/app_localizations.dart';
import 'package:movie_app/presentation/blocs/language/language_bloc.dart';
import 'package:movie_app/presentation/journeys/home/home_screen.dart';
class MovieApp extends StatefulWidget {
#override
_MovieAppState createState() => _MovieAppState();
}
class _MovieAppState extends State<MovieApp> {
LanguageBloc _languageBloc;
#override
void initState() {
_languageBloc = LanguageBloc();
super.initState();
}
#override
void dispose() {
_languageBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return BlocProvider<LanguageBloc>.value(
value: _languageBloc,
child: BlocBuilder<LanguageBloc, LanguageState>(
builder: (context, state) {
if (state is LanguageLoaded) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Movie App',
home: HomeScreen(),
supportedLocales:
Languages.languages.map((e) => Locale(e.code)).toList(),
locale: state.locale,
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
);
}
return SizedBox.shrink();
},
),
);
}
}
Now create Languages Language models and constants
class LanguageEntity {
final String code;
final String value;
const LanguageEntity({
this.code,
this.value,
});
}
class Languages {
const Languages._();
static const languages = [
LanguageEntity(code: 'en', value: 'English'),
LanguageEntity(code: 'es', value: 'Spanish'),
];
}
Now Create app localization delegate
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:movie_app/common/constants/languages.dart';
class AppLocalizations {
final Locale locale;
AppLocalizations(this.locale);
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationDelagate();
static AppLocalizations of(context) =>
Localizations.of<AppLocalizations>(context, AppLocalizations);
Map<String, String> _localisedString;
Future<bool> load() async {
final jsonString = await rootBundle
.loadString('assets/languages/${locale.languageCode}.json');
final Map<String, dynamic> jsonMap = json.decode(jsonString);
_localisedString =
jsonMap.map((key, value) => MapEntry(key, value.toString()));
return true;
}
String translate(String key) {
return _localisedString[key];
}
}
class _AppLocalizationDelagate extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationDelagate();
#override
bool isSupported(Locale locale) {
return Languages.languages
.map((e) => e.code)
.toList()
.contains(locale.languageCode);
}
#override
bool shouldReload(covariant LocalizationsDelegate old) {
return false;
}
#override
Future<AppLocalizations> load(Locale locale) async {
AppLocalizations appLocalizations = AppLocalizations(locale);
await appLocalizations.load();
return appLocalizations;
}
}
Now create blocs
import 'dart:async';
import 'package:bloc/bloc.dart';
// import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:movie_app/common/constants/languages.dart';
import 'package:movie_app/domain/entities/language_entity.dart';
part 'language_event.dart';
part 'language_state.dart';
class LanguageBloc extends Bloc<LanguageEvent, LanguageState> {
LanguageBloc() : super(LanguageLoaded(Locale(Languages.languages[0].code)));
#override
Stream<LanguageState> mapEventToState(
LanguageEvent event,
) async* {
if (event is ToggleLanguageEvent) {
yield LanguageLoaded(Locale(event.language.code));
}
}
}
8.Now create event
part of 'language_bloc.dart';
abstract class LanguageEvent {
const LanguageEvent();
}
class ToggleLanguageEvent extends LanguageEvent {
final LanguageEntity language;
ToggleLanguageEvent(this.language);
}
Now create state
part of 'language_bloc.dart';
abstract class LanguageState {
const LanguageState();
}
class LanguageLoaded extends LanguageState {
final Locale locale;
LanguageLoaded(this.locale);
}
10.Now Create button to change languages.
RaisedButton(child: ,RaisedButton(child: Text('Switch',
onPressed: (int index) {
BlocProvider.of<LanguageBloc>(context).add(
ToggleLanguageEvent(
Languages.languages[index], // index value can be 0 or 1 in our case
), // 0 - en, 1 - es
);
Navigator.of(context).pop();
},
);
Also, please refer the link for clear implementation
https://www.youtube.com/watch?v=W-2p3zB1z8k
If you are using provider as state management then you can follow this article .
https://medium.com/flutter-community/flutter-internationalization-the-easy-way-using-provider-and-json-c47caa4212b2
Because accepted answer has some shortcomings I will set new one:
Change locale of App can be done in several ways, here i will show two way:
Way 1 (using rxdart and SharedPreferences plugins):
For more understanding i was created project to explain how do that, you can find it in https://github.com/AnasSafi/flutter_localization_example
Output of project:
Way 2:
Note 1: I was used SharedPreferences plugin to save locale which
client select.
Note 2: Replace ar and ps with your default locale ...
Full code of main.dart file:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized(); //To solve problem (ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized)
runApp(MainApp());
}
class MainApp extends StatefulWidget {
const MainApp({Key? key}) : super(key: key);
/*
To Change Locale of App
*/
static void setLocale(BuildContext context, Locale newLocale) async {
_MainAppState? state = context.findAncestorStateOfType<_MainAppState>();
var prefs = await SharedPreferences.getInstance();
prefs.setString('languageCode', newLocale.languageCode);
prefs.setString('countryCode', "");
state?.setState(() {
state._locale = newLocale;
});
}
#override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
Locale _locale = Locale('ar', 'ps');
#override
void initState() {
super.initState();
this._fetchLocale().then((locale) {
setState(() {
this._locale = locale;
});
});
}
/*
To get local from SharedPreferences if exists
*/
Future<Locale> _fetchLocale() async {
var prefs = await SharedPreferences.getInstance();
String languageCode = prefs.getString('languageCode') ?? 'ar';
String countryCode = prefs.getString('countryCode') ?? 'ps';
return Locale(languageCode, countryCode);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
locale: _locale,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
const FallbackCupertinoLocalisationsDelegate(),
],
supportedLocales: [
const Locale('en', ''), // English, no country code
const Locale('ar', ''), // Arabic, no country code
],
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: InitializeApp(), // here use your own home name...
);
}
}
/*
To solve problem of hold press on inputs
*/
class FallbackCupertinoLocalisationsDelegate extends LocalizationsDelegate<CupertinoLocalizations> {
const FallbackCupertinoLocalisationsDelegate();
#override
bool isSupported(Locale locale) => true;
#override
Future<CupertinoLocalizations> load(Locale locale) =>
DefaultCupertinoLocalizations.load(locale);
#override
bool shouldReload(FallbackCupertinoLocalisationsDelegate old) => false;
}
Now from any other class you need to change locale from it, you can use my way:
Import main.dart file, Then use my simple DropdownButton:
Note: replace value:
AppLocalizations.of(context)!.locale.toString(), this line with your
way to get current locale of app...
import 'package:yout_project_name/main.dart';
DropdownButton(
onChanged: (v) => setState(() {
MainApp.setLocale(context, Locale(v.toString(), ""));
}),
value: AppLocalizations.of(context)!.locale.toString(), // change this line with your way to get current locale to select it as default in dropdown
items: [
DropdownMenuItem(
child: Text( 'English'), value: 'en'
),
DropdownMenuItem(
child: Text( 'العربية'), value: 'ar'
),
],
)
Take a look at the language_builder package in pub.dev
It is very easy to use. By wrapping your root widget with LanguageBuilder you can configure your app's language.
Tell your app to use the phones' language or change it manually from the app.
https://pub.dev/packages/language_builder
In Flutter we can customize the view-change animation by extending PageRoute (or by using a class which extends that).
For instance, I'm changing the animation to "slide" in my MaterialApplication by using the CupertinoPageRoute that way:
Navigator.of(context).pushReplacement(
CupertinoPageRoute(builder: (context) => Calendar()),
);
Now I want to change that by using named views defined in the main.dart file:
return MaterialApp(
title: 'Demo',
theme: myTheme, // => Theme.of(context).copyWith(...)
initialRoute: '/',
routes: {
'/': (context) => Login(),
'/calendar': (context) => Calendar(),
}
);
This way I can just call
Navigator.of(context).pushReplacementNamed('/calendar');
Which is IMO clearer and view-agnostic.
The issue with this approach is that I can't define a PageRoute, so I can't customize the view-change animation.
Is there a way to do that?
I took chemamolins' advice and solved it in a similar way, but using maps.
I "extracted" the routes object and put it outside MaterialApp:
var routes = {
'/': (context) => Login(),
'/calendar': (context) => Calendar()
};
Then I used it inside onGenerateRoute:
Widget build(BuildContext context) {
var routes = {
'/': (context) => Login(),
'/calendar': (context) => Calendar()
};
return MaterialApp(
title: 'Demo',
theme: myTheme,
initialRoute: '/',
onGenerateRoute: (settings) {
return CupertinoPageRoute(builder: (context) => routes[settings.name](context));
}
);
}
You could leverage onGenerateRoute()
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
Route onGenerateRoute(RouteSettings settings) {
Route page;
switch (settings.name) {
case "/":
page = CupertinoPageRoute(builder: (context) => Login());
break;
case "/calendar":
page = CupertinoPageRoute(builder: (context) => Calendar());
break;
}
return page;
}
#override
Widget build(BuildContext context) {
return new WidgetsApp(
onGenerateRoute: onGenerateRoute,
initialRoute: "/",
);
}
}
Let's give a look to the Flutter code itself.
There is a framework provided onGenerateRoute() method called to generate the routes.
Look at the following snippet taken from the app.dart file in the framework.
Route<dynamic> _onGenerateRoute(RouteSettings settings) {
final String name = settings.name;
WidgetBuilder builder;
if (name == Navigator.defaultRouteName && widget.home != null) {
builder = (BuildContext context) => widget.home;
} else {
builder = widget.routes[name];
}
if (builder != null) {
return new MaterialPageRoute<dynamic>(
builder: builder,
settings: settings,
);
}
if (widget.onGenerateRoute != null)
return widget.onGenerateRoute(settings);
return null;
}
If the routes: provides a builder for a given name, it is used to generate the route using MaterialPageRoute by default. If it is not provided, it goes to generate it by using your onGenerateRoute() method.