mirror of
https://github.com/Iconica-Development/flutter_login_widget.git
synced 2025-05-19 13:43:44 +02:00
autologin
This commit is contained in:
parent
9d6529538d
commit
7abc81593b
14 changed files with 222 additions and 432 deletions
|
@ -1,42 +1,15 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../login_config.dart';
|
||||
import '../model/login_confirmation_result.dart';
|
||||
import '../model/login_user.dart';
|
||||
|
||||
abstract class LoginRepository with ChangeNotifier {
|
||||
String? _loggedIn = '';
|
||||
abstract class LoginRepository {
|
||||
bool loggedIn = false;
|
||||
String loginError = '';
|
||||
bool _initialized = false;
|
||||
|
||||
bool isInitialized() => _initialized;
|
||||
|
||||
/// This function returns true if the user is logged in.
|
||||
bool isLoggedIn() => _loggedIn != null && _loggedIn != '';
|
||||
String get user => _loggedIn!;
|
||||
|
||||
/// This function sets the logged in user.
|
||||
void setLoggedIn(String user) => _loggedIn = user;
|
||||
|
||||
String getLoginError() => loginError;
|
||||
|
||||
Future<bool> login(String username, String password);
|
||||
|
||||
Future<void> logout() async {
|
||||
_loggedIn = null;
|
||||
}
|
||||
|
||||
/// This function returns a map with the username.
|
||||
Map<String, dynamic> getUser() => {
|
||||
'username': _loggedIn,
|
||||
};
|
||||
|
||||
@mustCallSuper
|
||||
Future<void> init() async {
|
||||
// Auto login here
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
Future<LoginUser?> signInWithSocial(SocialLoginBundle bundle);
|
||||
Future<bool?> userprofileExists();
|
||||
Future sendLoginEmail(String input);
|
||||
|
@ -59,6 +32,6 @@ abstract class LoginRepository with ChangeNotifier {
|
|||
});
|
||||
Future<bool> forgotPassword(String email);
|
||||
Future<bool> isRegistrationRequired(LoginUser user);
|
||||
Future<void> reLogin();
|
||||
Future<void> reLogin({required VoidCallback onLoggedIn});
|
||||
Future<LoginUser?> signInAnonymous();
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import 'sdk/screen.dart';
|
||||
import 'sdk/user.dart';
|
||||
|
||||
mixin FlutterLoginSdk {
|
||||
static final UserService _userService = UserService();
|
||||
static final ScreenService _screenService = ScreenService();
|
||||
|
||||
UserService get users => _userService;
|
||||
ScreenService get screens => _screenService;
|
||||
|
||||
static UserService get userService => _userService;
|
||||
static ScreenService get screenService => _screenService;
|
||||
|
||||
void dispose() {
|
||||
_userService.dispose();
|
||||
}
|
||||
}
|
|
@ -2,10 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_login/backend/login_repository.dart';
|
||||
import '../default_translation.dart';
|
||||
import '../plugins/login/login_email_password.dart';
|
||||
import 'flutter_login_sdk.dart';
|
||||
import 'login_config.dart';
|
||||
import 'plugins/login/choose_login.dart';
|
||||
import 'widgets/custom_navigator.dart';
|
||||
export '../plugins/form/form.dart';
|
||||
export '../plugins/login/email_password_form.dart';
|
||||
export '../plugins/login/login_email_password.dart';
|
||||
|
@ -15,8 +13,8 @@ export 'model/login_confirmation_result.dart';
|
|||
export 'model/login_user.dart';
|
||||
export 'plugins/settings/control.dart' show Control;
|
||||
|
||||
class FlutterLogin extends InheritedWidget with FlutterLoginSdk {
|
||||
FlutterLogin({
|
||||
class FlutterLogin extends InheritedWidget {
|
||||
const FlutterLogin({
|
||||
required this.config,
|
||||
required this.repository,
|
||||
required Widget child,
|
||||
|
@ -24,14 +22,6 @@ class FlutterLogin extends InheritedWidget with FlutterLoginSdk {
|
|||
Key? key,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
FlutterLogin.from({
|
||||
required FlutterLogin appShell,
|
||||
required Widget child,
|
||||
Key? key,
|
||||
}) : config = appShell.config,
|
||||
repository = appShell.repository,
|
||||
app = appShell.app,
|
||||
super(child: child, key: key);
|
||||
static Function(Object?) logError = (error) {};
|
||||
static Map<String, Map<String, String>> get defaultTranslations =>
|
||||
defaultTranslation;
|
||||
|
@ -41,21 +31,21 @@ class FlutterLogin extends InheritedWidget with FlutterLoginSdk {
|
|||
final Widget app;
|
||||
|
||||
static FlutterLogin of(BuildContext context) {
|
||||
var inheritedAppshell =
|
||||
var inheritedLogin =
|
||||
context.dependOnInheritedWidgetOfExactType<FlutterLogin>();
|
||||
if (inheritedAppshell == null) {
|
||||
if (inheritedLogin == null) {
|
||||
throw FlutterError(
|
||||
'You are retrieving an flutter login from a context that does not contain an flutter login. Make sure you keep the flutter login in your inheritence tree',
|
||||
);
|
||||
}
|
||||
return inheritedAppshell;
|
||||
return inheritedLogin;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(FlutterLogin oldWidget) => config != oldWidget.config;
|
||||
}
|
||||
|
||||
extension AppShellRetrieval on BuildContext {
|
||||
extension LoginRetrieval on BuildContext {
|
||||
static LoginRepository? _cachedBackend;
|
||||
FlutterLogin login() => FlutterLogin.of(this);
|
||||
LoginRepository loginRepository() {
|
||||
|
@ -77,7 +67,7 @@ extension StringFormat on String {
|
|||
String format(List<dynamic> params) => _interpolate(this, params);
|
||||
}
|
||||
|
||||
extension AppShellTranslate on BuildContext {
|
||||
extension LoginTranslate on BuildContext {
|
||||
String? _getDefaultTranslation(String key, List arguments) {
|
||||
var locale = Localizations.localeOf(this);
|
||||
var code = locale.countryCode ?? 'nl';
|
||||
|
@ -86,8 +76,11 @@ extension AppShellTranslate on BuildContext {
|
|||
return translationMap?[key]?.toString().format(arguments);
|
||||
}
|
||||
|
||||
String translate(String key,
|
||||
{String? defaultValue, List<dynamic> arguments = const []}) {
|
||||
String translate(
|
||||
String key, {
|
||||
String? defaultValue,
|
||||
List<dynamic> arguments = const [],
|
||||
}) {
|
||||
dynamic translateFunction = login().config.translate;
|
||||
if (translateFunction == null) {
|
||||
return _getDefaultTranslation(key, arguments) ?? defaultValue ?? key;
|
||||
|
@ -100,21 +93,39 @@ extension AppShellTranslate on BuildContext {
|
|||
}
|
||||
}
|
||||
|
||||
class LoginMain extends StatelessWidget {
|
||||
LoginMain({
|
||||
class LoginMain extends StatefulWidget {
|
||||
const LoginMain({
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
final Widget child;
|
||||
|
||||
Widget _login(context) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
if (context.login().users.isLoggedIn(context)) {
|
||||
return child;
|
||||
@override
|
||||
State<LoginMain> createState() => _LoginMainState();
|
||||
}
|
||||
|
||||
return FlutterLogin.of(context)
|
||||
class _LoginMainState extends State<LoginMain> {
|
||||
bool _checkedIfLoggedIn = false;
|
||||
bool _isLoggedIn = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_checkedIfLoggedIn) {
|
||||
context.loginRepository().reLogin(
|
||||
onLoggedIn: () => setState(
|
||||
() {
|
||||
_isLoggedIn = true;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
_checkedIfLoggedIn = true;
|
||||
}
|
||||
|
||||
return _isLoggedIn
|
||||
? widget.child
|
||||
: Builder(
|
||||
builder: (context) => FlutterLogin.of(context)
|
||||
.config
|
||||
.loginOptions
|
||||
.loginMethod
|
||||
|
@ -125,28 +136,21 @@ class LoginMain extends StatelessWidget {
|
|||
.loginMethod
|
||||
.contains(LoginMethod.LoginInteractiveWithPhoneNumber)
|
||||
? ChooseLogin(
|
||||
child: child,
|
||||
child: widget.child,
|
||||
)
|
||||
: EmailPasswordLogin(
|
||||
onPressedForgotPassword: FlutterLogin.of(context)
|
||||
.config
|
||||
.loginOptions
|
||||
.onPressForgotPassword,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: widget.child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
CustomNavigator build(BuildContext context) => CustomNavigator(
|
||||
pageRoute: PageRoutes.materialPageRoute,
|
||||
home: _login(context),
|
||||
);
|
||||
}
|
||||
|
||||
class AppShellException implements Exception {
|
||||
AppShellException(
|
||||
class LoginException implements Exception {
|
||||
LoginException(
|
||||
this.error, [
|
||||
this.stackTrace = StackTrace.empty,
|
||||
]);
|
||||
|
@ -158,7 +162,7 @@ class AppShellException implements Exception {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Unhandled error occurred in Appshell: $error\n'
|
||||
return 'Unhandled error occurred in login: $error\n'
|
||||
'$stackTrace';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -337,7 +337,7 @@ class LoginConfig extends StatefulWidget {
|
|||
}
|
||||
|
||||
class LoginConfigState extends State<LoginConfig> with WidgetsBindingObserver {
|
||||
FlutterLogin? appShell;
|
||||
FlutterLogin? login;
|
||||
late final ConfigData configData;
|
||||
late LoginRepository repository;
|
||||
|
||||
|
@ -352,7 +352,6 @@ class LoginConfigState extends State<LoginConfig> with WidgetsBindingObserver {
|
|||
|
||||
if (widget.repository != null) {
|
||||
repository = widget.repository!;
|
||||
repository.init();
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
@ -383,7 +382,7 @@ class LoginConfigState extends State<LoginConfig> with WidgetsBindingObserver {
|
|||
!configData.loginOptions.socialOptions.forceAppleSignin))) {
|
||||
// check if apple login is removed by developer
|
||||
if (configData.loginOptions.socialOptions.socialLogins.isEmpty) {
|
||||
throw AppShellException(
|
||||
throw LoginException(
|
||||
'If you enable LoginMethod.LoginInteractiveWithSocial you must provide atleast 1 social login option!');
|
||||
}
|
||||
}
|
||||
|
@ -391,7 +390,7 @@ class LoginConfigState extends State<LoginConfig> with WidgetsBindingObserver {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
appShell = FlutterLogin(
|
||||
login = FlutterLogin(
|
||||
config: widget.config ?? ConfigData.example(),
|
||||
repository: repository,
|
||||
app: widget.child,
|
||||
|
@ -406,21 +405,10 @@ class LoginConfigState extends State<LoginConfig> with WidgetsBindingObserver {
|
|||
if (isFlutterDefaultTheme(context)) {
|
||||
return Theme(
|
||||
data: defaultTheme,
|
||||
child: appShell!,
|
||||
child: login!,
|
||||
);
|
||||
}
|
||||
|
||||
return appShell!;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
repository.dispose();
|
||||
return login!;
|
||||
}
|
||||
}
|
||||
|
||||
class AppshellNoDisplayError {
|
||||
AppshellNoDisplayError(this.error);
|
||||
Object error;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ class ChooseLogin extends Login {
|
|||
static String? finalEmail;
|
||||
static String? finalPassword;
|
||||
|
||||
createState() => ChooseLoginState();
|
||||
@override
|
||||
ChooseLoginState createState() => ChooseLoginState();
|
||||
}
|
||||
|
||||
class ChooseLoginState extends LoginState<ChooseLogin> {
|
||||
|
|
|
@ -47,11 +47,7 @@ class ForgotPasswordState extends State<ForgotPassword> {
|
|||
context.translate('forgot_password.text.title'),
|
||||
),
|
||||
),
|
||||
body: context.login().screens.getAppshellScreenWrapper(
|
||||
context,
|
||||
backgroundImg:
|
||||
context.login().config.loginOptions.backgroundImage,
|
||||
child: Column(
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
|
@ -62,12 +58,7 @@ class ForgotPasswordState extends State<ForgotPassword> {
|
|||
top: 27,
|
||||
left: 5,
|
||||
),
|
||||
child: context
|
||||
.login()
|
||||
.config
|
||||
.appTheme
|
||||
.buttons
|
||||
.backButton(
|
||||
child: context.login().config.appTheme.buttons.backButton(
|
||||
context: context,
|
||||
),
|
||||
),
|
||||
|
@ -111,10 +102,8 @@ class ForgotPasswordState extends State<ForgotPassword> {
|
|||
context.translate(
|
||||
'forgot_password.error.email_does_not_exist',
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText2!
|
||||
.copyWith(
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyText2!.copyWith(
|
||||
color: Theme.of(context).errorColor,
|
||||
),
|
||||
),
|
||||
|
@ -131,12 +120,7 @@ class ForgotPasswordState extends State<ForgotPassword> {
|
|||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 30),
|
||||
padding: const EdgeInsets.only(bottom: 40.0),
|
||||
child: context
|
||||
.login()
|
||||
.config
|
||||
.appTheme
|
||||
.buttons
|
||||
.primaryButton(
|
||||
child: context.login().config.appTheme.buttons.primaryButton(
|
||||
context: context,
|
||||
child: Text(
|
||||
context.translate('forgot_password.button.submit'),
|
||||
|
@ -180,7 +164,6 @@ class ForgotPasswordState extends State<ForgotPassword> {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,30 +15,16 @@ class LoginAwaitEmailScreen extends StatefulWidget {
|
|||
final Widget? child;
|
||||
|
||||
@override
|
||||
_LoginAwaitEmailScreenState createState() => _LoginAwaitEmailScreenState();
|
||||
LoginAwaitEmailScreenState createState() => LoginAwaitEmailScreenState();
|
||||
}
|
||||
|
||||
class _LoginAwaitEmailScreenState extends State<LoginAwaitEmailScreen>
|
||||
class LoginAwaitEmailScreenState extends State<LoginAwaitEmailScreen>
|
||||
with NavigateWidgetMixin {
|
||||
@override
|
||||
void initState() {
|
||||
context.loginRepository().addListener(registrateOrMainScreen);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> registrateOrMainScreen() async {
|
||||
var data =
|
||||
await (context.loginRepository().userprofileExists() as FutureOr<bool>);
|
||||
if (context.login().config.registrationOptions.registrationMode ==
|
||||
RegistrationMode.Disabled ||
|
||||
data) {
|
||||
context.loginRepository().setLoggedIn(EmailPasswordLogin.finalEmail!);
|
||||
widget.loginComplete!();
|
||||
} else {
|
||||
debugPrint('Register');
|
||||
}
|
||||
}
|
||||
|
||||
void navigateToEmailPage(BuildContext context) {
|
||||
navigateFadeTo(
|
||||
context,
|
||||
|
|
|
@ -87,7 +87,7 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
|
|||
_loading = false;
|
||||
});
|
||||
} else {
|
||||
context.loginRepository().setLoggedIn(EmailPasswordLogin.finalEmail!);
|
||||
context.loginRepository().loggedIn = true;
|
||||
navigateFadeToReplace(
|
||||
context,
|
||||
(context) => widget.child,
|
||||
|
|
|
@ -14,7 +14,6 @@ class LoginImage extends StatelessWidget {
|
|||
if (config.loginImage == '') {
|
||||
image = const AssetImage(
|
||||
'assets/images/login.png',
|
||||
package: 'appshell',
|
||||
);
|
||||
} else if (split.length < 2) {
|
||||
image = AssetImage(config.loginImage);
|
||||
|
|
|
@ -36,11 +36,7 @@ abstract class LoginState<L extends Login> extends State<L>
|
|||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
backgroundColor: Theme.of(context).backgroundColor,
|
||||
body: context.login().screens.getAppshellScreenWrapper(
|
||||
context,
|
||||
backgroundImg:
|
||||
context.login().config.loginOptions.backgroundImage,
|
||||
child: SizedBox(
|
||||
body: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Stack(
|
||||
children: [
|
||||
|
@ -72,7 +68,6 @@ abstract class LoginState<L extends Login> extends State<L>
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Widget buildLoginPage(BuildContext context);
|
||||
|
|
|
@ -5,10 +5,10 @@ class Resend extends StatefulWidget {
|
|||
const Resend({super.key});
|
||||
|
||||
@override
|
||||
_ConfirmationState createState() => _ConfirmationState();
|
||||
ConfirmationState createState() => ConfirmationState();
|
||||
}
|
||||
|
||||
class _ConfirmationState extends State<Resend> {
|
||||
class ConfirmationState extends State<Resend> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import '../flutter_login_view.dart';
|
||||
|
||||
class ScreenService {
|
||||
late bool shouldShowIntroductionScreen;
|
||||
late bool shouldShowPolicyPage;
|
||||
|
||||
Widget getAppshellScreenWrapper(
|
||||
BuildContext context, {
|
||||
required Widget child,
|
||||
String? backgroundImg,
|
||||
}) {
|
||||
var bgImage =
|
||||
backgroundImg ?? context.login().config.appOptions.backgroundImage;
|
||||
if (bgImage.isNotEmpty) {
|
||||
late AssetImage image;
|
||||
var split = bgImage.split(';');
|
||||
|
||||
image = split.length < 2
|
||||
? AssetImage(bgImage)
|
||||
: AssetImage(
|
||||
split.first,
|
||||
package: split.last,
|
||||
);
|
||||
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: image,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../flutter_login_view.dart';
|
||||
|
||||
class UserService extends ChangeNotifier {
|
||||
late Map<String, dynamic>? _currentProfile = {};
|
||||
|
||||
set profile(Map<String, dynamic> profile) {
|
||||
_currentProfile = profile;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> checkAutoLogin(FlutterLogin login) async {
|
||||
debugPrint('checking autologin');
|
||||
if (login.config.loginOptions.loginMode != LoginMode.NoLogin) {
|
||||
await Future.delayed(const Duration(milliseconds: 100), () async {
|
||||
if (login.config.loginOptions.loginMode == LoginMode.LoginAutomatic) {
|
||||
if (login.config.loginOptions.loginEmail == null ||
|
||||
login.config.loginOptions.loginEmail == '') {
|
||||
throw Exception('No login account for automatic login provided!');
|
||||
}
|
||||
if (login.config.loginOptions.loginPassword == null ||
|
||||
login.config.loginOptions.loginPassword == '') {
|
||||
throw Exception(
|
||||
'No login password for automatic login provided!',
|
||||
);
|
||||
}
|
||||
await login.repository.login(login.config.loginOptions.loginEmail!,
|
||||
login.config.loginOptions.loginPassword!);
|
||||
} else if (login.config.loginOptions.loginMode ==
|
||||
LoginMode.LoginAnonymous) {
|
||||
await login.repository.signInAnonymous();
|
||||
} else {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
var autoLoginMode = login.config.loginOptions.autoLoginMode;
|
||||
if ((autoLoginMode != AutoLoginMode.alwaysOff &&
|
||||
(prefs.getBool('autoLogin') ?? false) == true) ||
|
||||
autoLoginMode == AutoLoginMode.alwaysOn) {
|
||||
await login.repository.reLogin();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void addProfileListener(
|
||||
void Function(Map<String, dynamic>) onProfileChanged,
|
||||
) {
|
||||
addListener(() {
|
||||
onProfileChanged.call(_currentProfile!);
|
||||
});
|
||||
}
|
||||
|
||||
bool isLoggedIn(BuildContext context) =>
|
||||
context.loginRepository().isLoggedIn();
|
||||
|
||||
Future<void> logout(BuildContext context) =>
|
||||
SharedPreferences.getInstance().then(
|
||||
(value) {
|
||||
value
|
||||
.setBool('autoLogin', false)
|
||||
.then((value) => context.loginRepository().logout());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class UserProfile {
|
||||
late Map<String, dynamic> rawFields;
|
||||
String? photoUrl;
|
||||
|
||||
@mustCallSuper
|
||||
void init(Map<String, dynamic> raw) {
|
||||
rawFields = raw;
|
||||
photoUrl = raw['photo'];
|
||||
}
|
||||
|
||||
bool isProfileComplete({List<String> requiredFields = const []}) {
|
||||
return !requiredFields.any((element) => rawFields[element] == null);
|
||||
}
|
||||
|
||||
dynamic getValue(String key) {
|
||||
return rawFields[key];
|
||||
}
|
||||
}
|
|
@ -24,10 +24,10 @@ class CustomNavigator extends StatefulWidget {
|
|||
final List<NavigatorObserver> navigatorObservers;
|
||||
|
||||
@override
|
||||
_CustomNavigatorState createState() => _CustomNavigatorState();
|
||||
CustomNavigatorState createState() => CustomNavigatorState();
|
||||
}
|
||||
|
||||
class _CustomNavigatorState extends State<CustomNavigator>
|
||||
class CustomNavigatorState extends State<CustomNavigator>
|
||||
implements WidgetsBindingObserver {
|
||||
GlobalKey<NavigatorState>? _navigator;
|
||||
|
||||
|
@ -134,9 +134,11 @@ class _CustomNavigatorState extends State<CustomNavigator>
|
|||
return result;
|
||||
}
|
||||
|
||||
didChangeAppLifecycleState(AppLifecycleState state) {}
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {}
|
||||
|
||||
noSuchMethod(Invocation invocation) {
|
||||
@override
|
||||
void noSuchMethod(Invocation invocation) {
|
||||
var name = invocation.memberName.toString();
|
||||
debugPrint(
|
||||
'Expected a method to be called with name $name, '
|
||||
|
@ -147,9 +149,9 @@ class _CustomNavigatorState extends State<CustomNavigator>
|
|||
|
||||
class PageRoutes {
|
||||
static final materialPageRoute =
|
||||
(<T>(RouteSettings settings, WidgetBuilder builder) =>
|
||||
MaterialPageRoute<T>(settings: settings, builder: builder));
|
||||
<T>(RouteSettings settings, WidgetBuilder builder) =>
|
||||
MaterialPageRoute<T>(settings: settings, builder: builder);
|
||||
static final cupertinoPageRoute =
|
||||
(<T>(RouteSettings settings, WidgetBuilder builder) =>
|
||||
CupertinoPageRoute<T>(settings: settings, builder: builder));
|
||||
<T>(RouteSettings settings, WidgetBuilder builder) =>
|
||||
CupertinoPageRoute<T>(settings: settings, builder: builder);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue