Web mobile : le clavier se refermait aussitot ouvert sur le login
Le meta viewport manquant n'expliquait pas tout : le clavier s'ouvrait puis se refermait apres une demi-seconde. C'est le focus qui etait perdu, pas la saisie qui n'arrivait pas. Sur Flutter web, un AutofillGroup se materialise par un <form> DOM qui porte les inputs caches par lesquels passe la saisie. L'ouverture du clavier redimensionne la fenetre, l'ecran se reconstruit, le groupe d'autofill est renegocie et le <form> recree : l'input focus disparait avec lui. Sur desktop rien ne redimensionne au clic, d'ou un bug invisible hors mobile. L'AutofillGroup est retire — les autofillHints portes par chaque champ suffisent au navigateur pour proposer le remplissage, le groupe ne servait qu'a finishAutofillContext. Trois protections completent la correction : - les deux champs passent par un TextEditingController detenu par le State, donc la saisie survit a une reconstruction, la ou initialValue repartait de zero ; - ils portent une ValueKey stable, pour etre reapparies plutot que recrees si la structure de leurs freres bouge ; - le Scaffold ne se redimensionne plus a l'ouverture du clavier ; le contenu est deja dans un SingleChildScrollView, rien ne reste masque. Au passage, le pre-remplissage de developpement (localhost) etait ecrit dans build() a chaque reconstruction : il passe dans initState, ou il a du sens. Le routeur, lui, etait fabrique dans le builder d'un FutureBuilder et l'appel a getInstanceInfo partait de l'arbre passe a runApp. Chaque passage du builder rendait un GoRouter neuf, donc un arbre neuf et un historique perdu. Les deux sont desormais resolus une fois, avant runApp — 45 lignes de moins. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
842f69ebc9
commit
0df5dc03fa
@ -14,6 +14,10 @@ class RoundedInputField extends StatelessWidget {
|
|||||||
final double fontSize;
|
final double fontSize;
|
||||||
final String? autofill;
|
final String? autofill;
|
||||||
final bool isInt;
|
final bool isInt;
|
||||||
|
|
||||||
|
/// A preferer a [initialValue] pour un champ vivant dans un ecran qui se
|
||||||
|
/// reconstruit : le texte survit alors a la reconstruction du TextFormField.
|
||||||
|
final TextEditingController? controller;
|
||||||
const RoundedInputField({
|
const RoundedInputField({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.hintText,
|
this.hintText,
|
||||||
@ -27,7 +31,8 @@ class RoundedInputField extends StatelessWidget {
|
|||||||
this.isEmail = false,
|
this.isEmail = false,
|
||||||
this.fontSize = 13,
|
this.fontSize = 13,
|
||||||
this.autofill,
|
this.autofill,
|
||||||
this.isInt = false
|
this.isInt = false,
|
||||||
|
this.controller,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -36,10 +41,11 @@ class RoundedInputField extends StatelessWidget {
|
|||||||
color: color,
|
color: color,
|
||||||
child: TextFormField (
|
child: TextFormField (
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
initialValue: initialValue,
|
controller: controller,
|
||||||
|
initialValue: controller == null ? initialValue : null,
|
||||||
cursorColor: textColor,
|
cursorColor: textColor,
|
||||||
maxLength: maxLength,
|
maxLength: maxLength,
|
||||||
autofillHints: autofill != null ? [autofill!] : [],
|
autofillHints: autofill != null ? [autofill!] : const [],
|
||||||
keyboardType: isEmail ? TextInputType.emailAddress : isInt ? TextInputType.number : TextInputType.text,
|
keyboardType: isEmail ? TextInputType.emailAddress : isInt ? TextInputType.number : TextInputType.text,
|
||||||
inputFormatters: !isInt ? [] : <TextInputFormatter>[
|
inputFormatters: !isInt ? [] : <TextInputFormatter>[
|
||||||
FilteringTextInputFormatter.digitsOnly
|
FilteringTextInputFormatter.digitsOnly
|
||||||
|
|||||||
@ -6,10 +6,12 @@ import 'package:manager_app/constants.dart';
|
|||||||
class RoundedPasswordField extends StatefulWidget {
|
class RoundedPasswordField extends StatefulWidget {
|
||||||
final ValueChanged<String> onChanged;
|
final ValueChanged<String> onChanged;
|
||||||
final String initialValue;
|
final String initialValue;
|
||||||
|
final TextEditingController? controller;
|
||||||
const RoundedPasswordField({
|
const RoundedPasswordField({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
required this.initialValue
|
required this.initialValue,
|
||||||
|
this.controller,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -25,8 +27,9 @@ class _RoundedPasswordFieldState extends State<RoundedPasswordField> {
|
|||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
obscureText: isVisible,
|
obscureText: isVisible,
|
||||||
onChanged: widget.onChanged,
|
onChanged: widget.onChanged,
|
||||||
initialValue: widget.initialValue,
|
controller: widget.controller,
|
||||||
autofillHints: [AutofillHints.password],
|
initialValue: widget.controller == null ? widget.initialValue : null,
|
||||||
|
autofillHints: const [AutofillHints.password],
|
||||||
cursorColor: kPrimaryColor,
|
cursorColor: kPrimaryColor,
|
||||||
style: TextStyle(fontSize: 16, color: kInk),
|
style: TextStyle(fontSize: 16, color: kInk),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import 'dart:html';
|
|||||||
|
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:manager_app/Components/common_loader.dart';
|
import 'package:manager_app/Components/common_loader.dart';
|
||||||
@ -44,6 +43,12 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
Storage localStorage = window.localStorage;
|
Storage localStorage = window.localStorage;
|
||||||
late final Future<String> appVersion = getAppVersion();
|
late final Future<String> appVersion = getAppVersion();
|
||||||
|
|
||||||
|
// Champs pilotes par un controller, et non par initialValue : l'ouverture du
|
||||||
|
// clavier mobile redimensionne la fenetre, ce qui reconstruit l'ecran. Avec
|
||||||
|
// initialValue, le TextFormField repartait de zero a chaque reconstruction.
|
||||||
|
final emailController = TextEditingController();
|
||||||
|
final passwordController = TextEditingController();
|
||||||
|
|
||||||
void authenticateTRY(AppContext appContext, bool fromClick) async {
|
void authenticateTRY(AppContext appContext, bool fromClick) async {
|
||||||
clientAPI = Client(this.host!);
|
clientAPI = Client(this.host!);
|
||||||
|
|
||||||
@ -188,9 +193,23 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
this.pinCode = localStorage.entries.where((e) => e.key == "pinCode").first.value;
|
this.pinCode = localStorage.entries.where((e) => e.key == "pinCode").first.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (window.location.href.contains("localhost")) {
|
||||||
|
email = "test@email.be";
|
||||||
|
password = "kljqsdkljqsd";
|
||||||
|
}
|
||||||
|
|
||||||
|
emailController.text = email;
|
||||||
|
passwordController.text = password;
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
emailController.dispose();
|
||||||
|
passwordController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
ManagerAppContext initInstance(ManagerAppContext managerAppContext) {
|
ManagerAppContext initInstance(ManagerAppContext managerAppContext) {
|
||||||
var url = window.location.href;
|
var url = window.location.href;
|
||||||
if (!url.contains("localhost")) {
|
if (!url.contains("localhost")) {
|
||||||
@ -209,8 +228,6 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
print("subdomain not found");
|
print("subdomain not found");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.email = "test@email.be";
|
|
||||||
this.password = "kljqsdkljqsd";
|
|
||||||
print("localhost.. set mymuseum instance by default");
|
print("localhost.. set mymuseum instance by default");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,6 +241,11 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
initInstance(appContext.getContext());
|
initInstance(appContext.getContext());
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
// Le Scaffold ne se redimensionne pas a l'ouverture du clavier : sur web
|
||||||
|
// mobile ce redimensionnement relance une mise en page pendant que le
|
||||||
|
// navigateur ouvre le clavier, et le focus n'y survit pas. Le contenu est
|
||||||
|
// deja dans un SingleChildScrollView, rien ne reste sous le clavier.
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
body: LayoutBuilder(
|
body: LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
final isMobile = constraints.maxWidth < 550;
|
final isMobile = constraints.maxWidth < 550;
|
||||||
@ -307,23 +329,31 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 32),
|
SizedBox(height: 32),
|
||||||
|
// Pas d'AutofillGroup ici : sur Flutter web il se
|
||||||
|
// materialise par un <form> DOM qui porte les inputs
|
||||||
|
// caches. La reconstruction declenchee par l'ouverture
|
||||||
|
// du clavier mobile le recreait, l'input focus etait
|
||||||
|
// detruit et le clavier se refermait aussitot. Les
|
||||||
|
// autofillHints des champs suffisent au navigateur.
|
||||||
Form(
|
Form(
|
||||||
key: widget.key,
|
key: widget.key,
|
||||||
child: AutofillGroup(
|
child: Column(
|
||||||
child: Column(
|
|
||||||
children: [
|
children: [
|
||||||
RoundedInputField(
|
RoundedInputField(
|
||||||
|
key: const ValueKey('login-email'),
|
||||||
hintText: AppLocalizations.of(context)!.email,
|
hintText: AppLocalizations.of(context)!.email,
|
||||||
autofill: AutofillHints.email,
|
autofill: AutofillHints.email,
|
||||||
onChanged: (value) => email = value,
|
onChanged: (value) => email = value,
|
||||||
icon: Icons.person_outline,
|
icon: Icons.person_outline,
|
||||||
initialValue: email,
|
controller: emailController,
|
||||||
isEmail: true,
|
isEmail: true,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
const SizedBox(height: kSpace4),
|
const SizedBox(height: kSpace4),
|
||||||
RoundedPasswordField(
|
RoundedPasswordField(
|
||||||
|
key: const ValueKey('login-password'),
|
||||||
initialValue: password,
|
initialValue: password,
|
||||||
|
controller: passwordController,
|
||||||
onChanged: (value) => password = value,
|
onChanged: (value) => password = value,
|
||||||
),
|
),
|
||||||
if (kIsWeb)
|
if (kIsWeb)
|
||||||
@ -357,10 +387,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
vertical: 15,
|
vertical: 15,
|
||||||
horizontal: 30,
|
horizontal: 30,
|
||||||
press: () {
|
press: () => authenticateTRY(appContext, true),
|
||||||
TextInput.finishAutofillContext();
|
|
||||||
authenticateTRY(appContext, true);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: CommonLoader(iconSize: 40),
|
: CommonLoader(iconSize: 40),
|
||||||
@ -374,7 +401,6 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
265
lib/main.dart
265
lib/main.dart
@ -11,10 +11,8 @@ import 'package:manager_app/Screens/Main/main_screen.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
import 'Components/common_loader.dart';
|
|
||||||
import 'Helpers/FileHelper.dart';
|
import 'Helpers/FileHelper.dart';
|
||||||
import 'Models/session.dart';
|
import 'Models/session.dart';
|
||||||
import 'Screens/Main/main_screen.dart';
|
|
||||||
import 'Screens/login_screen.dart';
|
import 'Screens/login_screen.dart';
|
||||||
import 'Screens/Auth/forgot_password_screen.dart';
|
import 'Screens/Auth/forgot_password_screen.dart';
|
||||||
import 'Screens/Auth/set_password_screen.dart';
|
import 'Screens/Auth/set_password_screen.dart';
|
||||||
@ -52,161 +50,118 @@ Future<void> main() async {
|
|||||||
|
|
||||||
usePathUrlStrategy();
|
usePathUrlStrategy();
|
||||||
|
|
||||||
runApp(
|
// getInstanceInfo etait appele depuis l'arbre passe a runApp, et le GoRouter
|
||||||
ChangeNotifierProvider<AppContext>(
|
// fabrique dans le builder d'un FutureBuilder : chaque passage du builder
|
||||||
create: (_) => AppContext(managerAppContext),
|
// rendait un routeur neuf, donc un arbre neuf et un historique de navigation
|
||||||
child: MaterialApp(
|
// perdu. Les deux sont desormais resolus une seule fois, avant runApp.
|
||||||
debugShowCheckedModeBanner: false,
|
var instanceDTO = await getInstanceInfo(managerAppContext);
|
||||||
locale: const Locale('fr'),
|
managerAppContext.instanceId = instanceDTO?.id;
|
||||||
supportedLocales: const [Locale('fr')],
|
managerAppContext.instanceDTO = instanceDTO;
|
||||||
localizationsDelegates: const [
|
|
||||||
AppLocalizations.delegate,
|
|
||||||
FlutterQuillLocalizations.delegate,
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
],
|
|
||||||
home: FutureBuilder(
|
|
||||||
future: getInstanceInfo(managerAppContext),
|
|
||||||
builder: (context, asyncSnapshot) {
|
|
||||||
if (asyncSnapshot.connectionState == ConnectionState.done) {
|
|
||||||
var instanceDTO = asyncSnapshot.data;
|
|
||||||
managerAppContext.instanceId = instanceDTO?.id;
|
|
||||||
managerAppContext.instanceDTO = instanceDTO;
|
|
||||||
|
|
||||||
String initialRoute = currentPath!.isNotEmpty ? currentPath : '/login';
|
runApp(
|
||||||
|
ChangeNotifierProvider<AppContext>(
|
||||||
var _router = GoRouter(
|
create: (_) => AppContext(managerAppContext),
|
||||||
initialLocation: initialRoute,
|
child: MyApp(
|
||||||
redirect: (context, state) {
|
session: session,
|
||||||
var instanceId = managerAppContext.instanceId;
|
managerAppContext: managerAppContext,
|
||||||
const publicPaths = ['/login', '/forgot-password', '/set-password'];
|
router: buildRouter(
|
||||||
if (instanceId == null && !publicPaths.contains(state.fullPath)) {
|
managerAppContext,
|
||||||
return '/login';
|
currentPath!.isNotEmpty ? currentPath : '/login',
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (instanceId != null) {
|
|
||||||
if(state.fullPath == '/login' || state.fullPath == "") {
|
|
||||||
if(managerAppContext.instanceDTO!.isMobile!) {
|
|
||||||
return '/main/mobile';
|
|
||||||
}
|
|
||||||
if(managerAppContext.instanceDTO!.isTablet!) {
|
|
||||||
return '/main/tablet';
|
|
||||||
}
|
|
||||||
if(managerAppContext.instanceDTO!.isWeb!) {
|
|
||||||
return '/main/web';
|
|
||||||
}
|
|
||||||
if(managerAppContext.instanceDTO!.isVR!) {
|
|
||||||
return '/main/vr';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state.matchedLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
path: '/login',
|
|
||||||
builder: (context, state) => LoginScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/forgot-password',
|
|
||||||
builder: (context, state) => ForgotPasswordScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/set-password',
|
|
||||||
builder: (context, state) => SetPasswordScreen(
|
|
||||||
token: state.uri.queryParameters['token'],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/main/:view',
|
|
||||||
builder: (context, state) {
|
|
||||||
final view = state.pathParameters['view'];
|
|
||||||
// La clé porte l'instance : sans elle, un SuperAdmin
|
|
||||||
// qui change d'instance en restant sur la même vue
|
|
||||||
// gardait le State de tous les écrans en dessous, donc
|
|
||||||
// les données de l'instance précédente.
|
|
||||||
return MainScreen(
|
|
||||||
key: ValueKey('${managerAppContext.instanceId}/$view'),
|
|
||||||
instance: managerAppContext.instanceDTO!,
|
|
||||||
view: view,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/policy',
|
|
||||||
builder: (context, state) => PolicyScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/policy/mdlf',
|
|
||||||
builder: (context, state) => PolicyScreen(param: "mdlf"),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/policy/fort',
|
|
||||||
builder: (context, state) => PolicyScreen(param: "fort"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
errorBuilder: (context, state) => MaterialApp(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
home: Scaffold(
|
|
||||||
body: Center(
|
|
||||||
child: SizedBox(
|
|
||||||
width: MediaQuery.of(context).size.width * 0.85,
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.pageNotFound,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: kPrimaryColor, fontSize: 20.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return MyApp(
|
|
||||||
session: session,
|
|
||||||
managerAppContext: managerAppContext,
|
|
||||||
router: _router
|
|
||||||
);
|
|
||||||
} else if (asyncSnapshot.connectionState == ConnectionState.none) {
|
|
||||||
return MaterialApp(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
home: Scaffold(
|
|
||||||
body: Center(
|
|
||||||
child: SizedBox(
|
|
||||||
width: MediaQuery.of(context).size.width * 0.85,
|
|
||||||
child: Text(
|
|
||||||
"No data",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: kPrimaryColor, fontSize: 20.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return MaterialApp(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
home: Scaffold(
|
|
||||||
body: Center(
|
|
||||||
child: SizedBox(
|
|
||||||
width: MediaQuery.of(context).size.width * 0.85,
|
|
||||||
child: Container(
|
|
||||||
height: 250,
|
|
||||||
child: CommonLoader()
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GoRouter buildRouter(ManagerAppContext managerAppContext, String initialLocation) {
|
||||||
|
return GoRouter(
|
||||||
|
initialLocation: initialLocation,
|
||||||
|
redirect: (context, state) {
|
||||||
|
var instanceId = managerAppContext.instanceId;
|
||||||
|
const publicPaths = ['/login', '/forgot-password', '/set-password'];
|
||||||
|
if (instanceId == null && !publicPaths.contains(state.fullPath)) {
|
||||||
|
return '/login';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (instanceId != null) {
|
||||||
|
if(state.fullPath == '/login' || state.fullPath == "") {
|
||||||
|
if(managerAppContext.instanceDTO!.isMobile!) {
|
||||||
|
return '/main/mobile';
|
||||||
|
}
|
||||||
|
if(managerAppContext.instanceDTO!.isTablet!) {
|
||||||
|
return '/main/tablet';
|
||||||
|
}
|
||||||
|
if(managerAppContext.instanceDTO!.isWeb!) {
|
||||||
|
return '/main/web';
|
||||||
|
}
|
||||||
|
if(managerAppContext.instanceDTO!.isVR!) {
|
||||||
|
return '/main/vr';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state.matchedLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: '/login',
|
||||||
|
builder: (context, state) => LoginScreen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/forgot-password',
|
||||||
|
builder: (context, state) => ForgotPasswordScreen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/set-password',
|
||||||
|
builder: (context, state) => SetPasswordScreen(
|
||||||
|
token: state.uri.queryParameters['token'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/main/:view',
|
||||||
|
builder: (context, state) {
|
||||||
|
final view = state.pathParameters['view'];
|
||||||
|
// La clé porte l'instance : sans elle, un SuperAdmin
|
||||||
|
// qui change d'instance en restant sur la même vue
|
||||||
|
// gardait le State de tous les écrans en dessous, donc
|
||||||
|
// les données de l'instance précédente.
|
||||||
|
return MainScreen(
|
||||||
|
key: ValueKey('${managerAppContext.instanceId}/$view'),
|
||||||
|
instance: managerAppContext.instanceDTO!,
|
||||||
|
view: view,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/policy',
|
||||||
|
builder: (context, state) => PolicyScreen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/policy/mdlf',
|
||||||
|
builder: (context, state) => PolicyScreen(param: "mdlf"),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/policy/fort',
|
||||||
|
builder: (context, state) => PolicyScreen(param: "fort"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
errorBuilder: (context, state) => MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.85,
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.pageNotFound,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: kPrimaryColor, fontSize: 20.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 3.1.3+11
|
version: 3.1.4+12
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.1.0 <4.0.0"
|
sdk: ">=3.1.0 <4.0.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user