if we use MaterialApp, we build like this:
Navigator(
key: naviKey,
onGenerateRoute: (routeSettings) => MaterialPageRoute(
builder: (context)=>Container(),
),
)
and now, how to build if we use Getx?
Routing in GetX can be setup like so. Note Page1.id is after putting static const id = 'page_1 in Page1 so you don't have to use raw strings.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
home: Page1(),
routes: {
Page1.id: (context) => Page1(),
Page2.id: (context) => Page2(),
},
);
}
}
or like this
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
home: Page1(),
getPages: [
GetPage(name: Page1.id, page: () => Page1()),
GetPage(name: Page1.id, page: () => Page1()),
],
);
}
}
You can setup the list of routes on another page if you don't want to clutter up your GetMaterialApp with all your routes.
Then when you want to navigate you can do it like this
Get.to(Page2());
Related
im new on flutter, and i experience some troubles after i add a new provider into my main.
this is my main code:
import 'package:catalo_gate/providers/providers.dart';
import 'package:catalo_gate/services/auth_service.dart';
import 'package:catalo_gate/services/products_service.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'screens/screens.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const AppState());
}
class AppState extends StatelessWidget {
const AppState({super.key});
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ProductsService()),
ChangeNotifierProvider(
create: (_) => LoginProvider(authService: AuthService())),
],
child: const MyApp(),
);
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'CataloGate',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: 'login',
routes: {
'login': (BuildContext context) => const LoginScreen(),
'register': (BuildContext context) => const RegisterScreen(),
'checking': (BuildContext context) => const CheckAuthScreen(),
'home': (BuildContext context) => const HomeScreen(),
'product': (BuildContext context) => const ProductScreen(),
'productsList': (BuildContext context) => const ProductsListScreen(),
//SubScreens
'listaReposicion': (BuildContext context) =>
const ListaDiasReposicionScreen(),
'entregas': (BuildContext context) => const DiasDeEntregaScreen(),
'unidadesPorBulto': (BuildContext context) =>
const UnidadesPorBultoScreen(),
},
);
}
}
everytime i try to make login i receive this message:
ProviderNotFoundException (Error: Could not find the correct Provider above this HomeScreen 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 HomeScreen is under your MultiProvider/Provider.
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>().toString()),
);
}
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, child) {
// No longer throws
return Text(context.watch<Example>().toString());
}
);
}
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
)
Can someone Help me??
i'd try a lot of options, changing the way to use the ChangeNotifierProvider for example
I want to reuse one bloc per router widget, but each widget already have a bloc, how to insert another bloc to it?
void main() => runApp(App());
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
final _sharedBloc = SharedBloc();
final _oneBloc = OneBloc();
final _twoBloc = TwoBloc();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
routes: {
'/one': (context) => BlocProvider.value(
value: _oneBloc, // add _sharedBloc to OnePage widget.
child: OnePage(),
),
'/two': (context) => BlocProvider.value(
value: _twoBloc, // add _sharedBloc to TwoPage widget.
child: TwoPage(),
),
},
);
}
#override
void dispose() {
_counterBloc.close();
super.dispose();
}
}
You can use MultiBlocProvider to provide multiple BLoC's at once to the child, as stated in the flutter_bloc documentation, like so:
routes: {
'/one': (context) => MultiBlocProvider(
providers: [
BlocProvider.value(
value: _sharedBloc,
),
BlocProvider.value(
value: _oneBloc,
),
],
child: OnePage(),
),
// '/two' is similar
},
I'm new to flutter, In MaterailApp Widget have an attribute called onUnknownRoute. What is the main use of onUnknownRoute?
Thanks for your help!
You can copy paste run full code below
In flutter web, when user manually key a undefine route, it can produce like 404 effect
full code
import 'package:flutter/material.dart';
void main() {
runApp( MaterialApp(
initialRoute: "/screen1",
routes: <String, WidgetBuilder>{
'/screen1': (BuildContext context) => Screen1(),
'/screen2': (BuildContext context) => Screen2(),
'/screen3': (BuildContext context) => Screen3(),
'/screen4': (BuildContext context) => Screen4()
},
onUnknownRoute: (RouteSettings settings) {
return MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) =>
Scaffold(body: Center(child: Text('Not Found'))),
);
},
)
);
}
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
class Screen3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
class Screen4 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
onUnknownRoute property is used to handle the worst case when the app navigation fails to get a route. If not handled, exception will be generated.
The navigator widget manages a stack of Route objects.
Route objects are nothing but full-screen elements like "screens" or "pages".
The navigator manages navigation between the screens in any app.
The Navigator for any app is only built if routes are provided by one of below items
home
routes
onGenerateRoute
onUnknownRoute
If the app only has one page, then you can specify it using home.
Else all routes will be in routes table including the home
If a route is requested that is not specified in routes table (or by home), then the onGenerateRoute callback is called
when even onGenerateRoute fails to generate a route, the property OnUnknownRoute will be triggered to handle the scenario.
We can handle an unknown route as below,
return MaterialApp(
title: 'Named Routing',
onGenerateRoute: router.generateRoute,
onUnknownRoute: (settings) => MaterialPageRoute(
builder: (context) => UndefinedView(
name: settings.name,
)),
initialRoute: HomeViewRoute,
);
If unknown route event is not handled using the onUnknownRoute property, flutter may throw an exception
The solution is coding in routes -> Example
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material App',
// home: HomePage(),
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => HomePage(),
'alert': (BuildContext context) => AlertPage(),
'avatar': (BuildContext context) => AvatarPage(),
},
***onUnknownRoute: (settings) {},***
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (BuildContext context) => AlertPage());
},
);
}
}
https://pub.dev/packages/provider
Can I use Provider to provide blocs globally to all widgets down the tree without MaterialApp? I tried to provide BLoC to a page, but when I'm navigating to the next page, BloC isn't found. So should I provide it whenever I'm navigating to a new page or is there a solution to provide it globally without MaterialApp?
Currently I'm doing it this way
Provider
|_MaterialApp
|_MyPage1 (from which you can navigate to MyPage2...3)
This approach works, and all pages can access provided BLoC.
But if use this approach
Provider
|_MyPage1 (from which you can navigate to MyPage2...3)
MyPage2, MyPage3 can't find provided BLoC. BLoC only can be found on MyPage1
You can achieve this by using the builder property of a MaterialApp.
lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SomeBloc {
final String someValue;
SomeBloc(this.someValue);
void dispose() {}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
SomeBloc _someBloc = SomeBloc("someValue");
#override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, widget) => Provider<SomeBloc>(
create: (_) => _someBloc,
dispose: (_, bloc) => bloc.dispose(),
child: widget,
),
initialRoute: '/first-page',
routes: <String, WidgetBuilder>{
'/first-page': (context) => FirstPage(),
'/second-page': (context) => SecondPage(),
},
);
}
}
class FirstPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
SomeBloc _someBloc = Provider.of<SomeBloc>(context);
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_someBloc.someValue),
RaisedButton(
child: Text("Open second page"),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => SecondPage(),
),
),
)
],
),
),
);
}
}
class SecondPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
SomeBloc _someBloc = Provider.of<SomeBloc>(context);
return Scaffold(
body: Center(
child: Text(_someBloc.someValue),
),
);
}
}
Per the documentation,
home
The difference between using home and using builder is that the home subtree is inserted into the application below a Navigator.
builder
... the widget returned from builder is inserted above the app's Navigator (if any).
If you want your "blocs" to be provided on all of your routes, you have to provide those above the Navigator widget.
That's why this simply works:
return Provider(
create: (_) => _someBloc,
child: MaterialApp(
home: FirstPage(),
),
);
In this setup above, Provider is on top of the Navigator widget.
And this doesn't:
return MaterialApp(
home: Provider(
create: (_) => _someBloc,
child: FirstPage(),
),
);
_someBloc will only be accessible to the FirstPage widget.
Hope this helps.
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.