GoRouter - Using ShellRoute properly in Flutter - flutter

I am using a ShellRoute within my GoRouter with those arguments:
final GoRouter _vamosRouter = GoRouter(
debugLogDiagnostics: true,
initialLocation: EventsPage.route,
errorBuilder: (_, __) => const ErrorPage(),
routes: [
GoRoute(
path: LoginPage.route,
builder: (_, __) => const LoginPage(),
// redirect: (context, state) => context.read<UserViewModel>().isSignedIn ? EventsPage.route : null, // ändert nix
),
ShellRoute(
navigatorKey: _rootNavigatorKey,
builder: (_, __, child) => RootPage(widget: child),
routes: <RouteBase>[
GoRoute(path: ProfilePage.route, builder: (_, __) => const ProfilePage()),
GoRoute(path: OrganizationPage.route, builder: (_, __) => const OrganizationPage()),
GoRoute(path: EventsPage.route, builder: (_, __) => const EventsPage()),
],
),
],
redirect: (BuildContext context, _) => context.read<UserViewModel>().isSignedIn ? null : LoginPage.route,
);
My Root Page looks like this:
class RootPage extends StatelessWidget {
const RootPage({required this.widget});
final Widget widget;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(children: [const Sidebar(), Expanded(child: widget)]),
);
}
}
So the basic layout is: a sidebar on the left side, and the main widget on the right site.
Problem: when switching between pages (or the main widget), the whole (RootPage) screen is built up again. How can I switch between pages in the ShellRoute with just the widget on the right site being reloaded?

Related

flutter package easyloading with ResponsiveWrapper

I have a problem. I'm using the easyloading package with responsivewrapper. but i can't put them together please help me
builder: EasyLoading.init(context, child) =>
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(360, 690),
minTextAdapt: true,
splitScreenMode: true,
builder: (context, child) {
return MaterialApp(
initialRoute: '/',
builder: EasyLoading.init(context, child) => ResponsiveWrapper.builder(child,
defaultScale: true,
breakpoints: [
ResponsiveBreakpoint.resize(480, name: MOBILE),
ResponsiveBreakpoint.autoScale(800, name: TABLET),
ResponsiveBreakpoint.resize(1000, name: DESKTOP),
],
background: Container(color: Color(0xFFF5F5F5))),
debugShowCheckedModeBanner: false,
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => MainPage(),
'/CusSearchAtcp': (BuildContext context) => const CusSearchAtcp(),
},
);
});
}
The builder parameter must return one widget. If you like to do initialization or return two widgets, you've to nest them yourself inside the builder:
builder: (context, child) {
// do your initialization here
child = EasyLoading.init(); // assuming this is returning a widget
child = ResponsiveWrapper.builder(/*your required code here*/);
return child;
}
Or you should try to use 2nd way by ResponsiveWrapper.builder or EasyLoading such as:
builder: EasyLoading.init(builder: ResponsiveWrapper.builder(/*your required code here*/)),

Go_Router Pass Object to new route

I want to pass object from the listview to the sub route. It seems no way to pass the object.
Is there any how to do it?
GoRouter routes(AuthBloc bloc) {
return GoRouter(
navigatorKey: _rootNavigatorKey,
routes: <RouteBase>[
GoRoute(
routes: <RouteBase>[
GoRoute(
path: loginURLPagePath,
builder: (BuildContext context, GoRouterState state) {
return const LoginPage();
},
),
GoRoute(
path: homeURLPagePath,
builder: (BuildContext context, GoRouterState state) =>
const HomePage(),
routes: <RouteBase>[
GoRoute(
path: feeURLPagePath,
name: 'a',
builder: (context, state) => FeePage(),
routes: [
/// Fee Details page
GoRoute(
name: 'b',
path: feeDetailsURLPagePath,
builder: (BuildContext context, GoRouterState state) =>
const FeeDetails(),
),
]),
],
),
],
path: welcomeURLPagePath,
builder: (BuildContext context, GoRouterState state) =>
const SplashPage(),
),
],
refreshListenable: GoRouterRefreshStream(bloc.stream),
debugLogDiagnostics: true,
initialLocation: welcomeURLPagePath,
},
);
}
The error says no initial match found for feeDetails!
Use extra parameter in context.goNamed()
Example:
Object:
class Sample {
String attributeA;
String attributeB;
bool boolValue;
Sample(
{required this.attributeA,
required this.attributeB,
required this.boolValue});}
Define GoRoute as
GoRoute(
path: '/sample',
name: 'sample',
builder: (context, state) {
Sample sample = state.extra as Sample; // -> casting is important
return GoToScreen(object: sample);
},
),
Call it as:
Sample sample = Sample(attributeA: "True",attributeB: "False",boolValue: false)
context.goNamed("sample",extra:sample );
Receive it as:
class GoToScreen extends StatelessWidget {
Sample? object;
GoToScreen({super.key, this.object});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(
object.toString(),
style: const TextStyle(fontSize: 24),
)),
);
}
}

Navigation to the screen where I am now

I need to update all the widgets on the screen, for this I decided to just make navigation to the screen I'm currently on. But unfortunately nothing happens. Tell me, what's the problem?
I am using the go_router package.
routes -
final GoRouter _router = GoRouter(
routes: <GoRoute>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) => Splash() ,
),
GoRoute(
path: '/home',
builder: (BuildContext context, GoRouterState state) => const Home() ,
),
GoRoute(
path: '/login',
builder: (BuildContext context, GoRouterState state) => const Login() ,
),
GoRoute(
path: '/createPinCode',
builder: (BuildContext context, GoRouterState state) => CreatePinCode() ,
),
],
button -
onCompleted: (value) async {
pinCode = int.parse(value);
sharedPrefsSet(pinCode);
context.go('/createPinCode');
},
If you just want to clear your form textFields than rather than re-build entire page you can user clear() property of TextEditingController.
TextEditingController nameController = TextEditingController();
func clearAllData() {
nameController.clear();
emailController.clear();
}
Call this clearAllData() when user tap on submit form.

Set default transition for go_router in Flutter

as the docs from go_router describe, it is easy to set pageBuilder-Transitions for single pages.
However, I want to set the default PageTransition for all pages.
How do I set the default page transition with/for go_router in Flutter?
Single Page:
// this is the proposed method to do it for single pages
// how can i apply it to all pages without writing the same code?
GoRoute(
path: '/login',
builder: (context, state) => const LoginScreen(),
pageBuilder: (context, state) => CustomTransitionPage<void>(
key: state.pageKey,
child: const LoginScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
FadeTransition(opacity: animation, child: child),
),
),
Best regards
The go_router package does not support this at the moment, but to reduce code duplication, you can create a helper function that would apply the custom transition for your route, like:
CustomTransitionPage buildPageWithDefaultTransition<T>({
required BuildContext context,
required GoRouterState state,
required Widget child,
}) {
return CustomTransitionPage<T>(
key: state.pageKey,
child: child,
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
FadeTransition(opacity: animation, child: child),
);
}
<...>
GoRoute(
path: '/login',
builder: (context, state) => const LoginScreen(),
pageBuilder: (context, state) => buildPageWithDefaultTransition<void>(
context: context,
state: state,
child: LoginScreen(),
),
),
The more correct way is inheritance from CustomTransitionPage.
class WebPage extends CustomTransitionPage {
WebPage({
LocalKey key,
... // other properties taken from `MaterialPage`
required Widget child
}) : super(
key: key,
transitionBuilder: (...) {
return FadeTransition(...);
}
child: child, // Here you may also wrap this child with some common designed widget
);
}
Then
GoRoute(
path: '/login',
pageBuilder: (context, state) => WebPage(
key: state.pageKey,
child: const LoginScreen(),
),
),

Flutter: Dynamic Initial Route

Dears,
I am using provider dart package which allows listeners to get notified on changes to models per se.
I am able to detect the change inside my main app root tree, and also able to change the string value of initial route however my screen is not updating. Kindly see below the code snippet and the comments lines:
void main() => runApp(_MyAppMain());
class _MyAppMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<UserProvider>.value(
value: UserProvider(),
),
ChangeNotifierProvider<PhoneProvider>.value(
value: PhoneProvider(),
)
],
child: Consumer<UserProvider>(
builder: (BuildContext context, userProvider, _) {
return FutureBuilder(
future: userProvider.getUser(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final User user = snapshot.data;
String initialScreen = LoginScreen.path;
// (1) I am able to get into the condition
if (user.hasActiveLogin()) {
initialScreen = HomeOneScreen.path;
}
return MaterialApp(
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.green,
accentColor: Colors.blueGrey,
),
initialRoute: initialScreen,
// (2) here the screen is not changing...?
routes: {
'/': (context) => null,
LoginScreen.path: (context) => LoginScreen(),
RegisterScreen.path: (context) => RegisterScreen(),
HomeOneScreen.path: (context) => HomeOneScreen(),
HomeTwoScreen.path: (context) => HomeTwoScreen(),
RegisterPhoneScreen.path: (context) => RegisterPhoneScreen(),
VerifyPhoneScreen.path: (context) => VerifyPhoneScreen(),
},
);
},
);
},
),
);
}
}
Kindly Note the Below:
These are are paths static const strings
LoginScreen.path = "login"
RegisterScreen.path = "/register-screen"
HomeOneScreen.path = "home-one-screen"
HomeTwoScreen.path = "home-two-screen"
RegisterPhoneScreen.path = "/register-phone-screen"
VerifyPhoneScreen.path = "/verify-phone-screen"
What I am missing for dynamic initialRoute to work?
Many Thanks
According to this issue described on github issues it is not permissible to have initial route changes. At least this is what I understood. However what I did is that I replaced the initialRoute attribute with home attr. Thus this change mandates that initialScreen becomes a widget var.
The changes is shown below:
void main() => runApp(_MyAppMain());
class _MyAppMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<UserProvider>.value(
value: UserProvider(),
),
ChangeNotifierProvider<PhoneProvider>.value(
value: PhoneProvider(),
)
],
child: Consumer<UserProvider>(
builder: (BuildContext context, userProvider, _) {
return FutureBuilder(
future: userProvider.getUser(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final User user = snapshot.data;
// (1) This becomes a widget
Widget initialScreen = LoginScreen();
if (user.hasActiveLogin()) {
initialScreen = HomeOneScreen();
}
return MaterialApp(
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.green,
accentColor: Colors.blueGrey,
),
home: initialScreen,
// (2) here the initial route becomes home attr.
routes: {
'/': (context) => null,
LoginScreen.path: (context) => LoginScreen(),
RegisterScreen.path: (context) => RegisterScreen(),
HomeOneScreen.path: (context) => HomeOneScreen(),
HomeTwoScreen.path: (context) => HomeTwoScreen(),
RegisterPhoneScreen.path: (context) => RegisterPhoneScreen(),
VerifyPhoneScreen.path: (context) => VerifyPhoneScreen(),
},
);
},
);
},
),
);
}
}
Also note on my RegistrationScreen on success api response I did Navigator.of(context).pop()
Thanks