#flutter
#флаттер
Вопрос:
Я пытаюсь это сделать, но это не кажется хорошим. Если я удалю FutureBuilder
и CircularProgressIndicator
, все в порядке. Но было бы неплохо иметь это на время загрузки данных. Возможно ли это?
Мой исходный код:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:serialtrip/config/theme.dart';
import 'package:serialtrip/pages/home.dart';
import 'package:serialtrip/pages/login.dart';
import 'package:serialtrip/providers/authProvider.dart';
main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AuthProvider(),
),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Serialtrip',
debugShowCheckedModeBanner: false,
theme: defaultTheme,
home: context.watch<AuthProvider>().loggedInStatus == Status.LoggedIn
? Home()
: FutureBuilder(
future: context.read<AuthProvider>().autoLogin(),
builder: (ctx, authResultSnapshot) =>
authResultSnapshot.connectionState == ConnectionState.waiting ? CircularProgressIndicator() : Login(),
),
);
}
}
И ошибка:
The following assertion was thrown building MyApp(dirty, dependencies: [_InheritedProviderScope<AuthProvider>]):
Tried to use `context.read<AuthProvider>` inside either a `build` method or the `update` callback of a provider.
This is unsafe to do so. Instead, consider using `context.watch<AuthProvider>`.
If you used `context.read` voluntarily as a performance optimisation, the solution
is instead to use `context.select`.
'package:provider/src/provider.dart':
Failed assertion: line 584 pos 9: 'debugIsInInheritedProviderCreate ||
(!debugDoingBuild amp;amp; !debugIsInInheritedProviderUpdate)'
Комментарии:
1. Вы пробовали использовать
FutureProvider
?2. Я пытаюсь, но я не знаю, как я использую FutureProvider: (
3. хорошо, хорошо, пожалуйста, предоставьте код для
AuthProvider
4. я ответил на новое сообщение ниже.
Ответ №1:
хорошо, я нашел решение. Я использую конструктор AuthProvider для вызова autoLogin() при первом запуске приложения.
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:serialtrip/config/theme.dart';
import 'package:serialtrip/pages/home.dart';
import 'package:serialtrip/pages/login.dart';
import 'package:serialtrip/pages/splash.dart';
import 'package:serialtrip/providers/authProvider.dart';
main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AuthProvider(),
),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Serialtrip',
debugShowCheckedModeBanner: false,
theme: defaultTheme,
home: _showScreen(context),
);
}
}
Widget _showScreen(BuildContext context) {
switch (context.watch<AuthProvider>().loggedInStatus) {
case Status.Authenticating:
return Splash();
case Status.LoggedIn:
return Home();
default:
return Login();
}
}
AuthProvider.dart
import 'package:flutter/widgets.dart';
enum Status { NotLoggedIn, Authenticating, LoggedIn }
class AuthProvider with ChangeNotifier {
Status _loggedInStatus = Status.NotLoggedIn;
Status get loggedInStatus => _loggedInStatus;
/// Constructor
AuthProvider() {
_autoLogin();
}
/// Auto-login
Future<void> _autoLogin() async {
_loggedInStatus = Status.Authenticating;
notifyListeners();
print('autologin - waiting');
await new Future.delayed(const Duration(seconds: 5));
_loggedInStatus = Status.LoggedIn;
notifyListeners();
print('autologin - sucess');
}
/// Login
Future<void> login(String email, String password) async {
_loggedInStatus = Status.Authenticating;
notifyListeners();
print('login - waiting');
await new Future.delayed(const Duration(seconds: 2));
_loggedInStatus = Status.LoggedIn;
notifyListeners();
print('login - sucess');
}
/// Logout
Future<void> logout() async {
_loggedInStatus = Status.NotLoggedIn;
notifyListeners();
print('logout - sucess');
}
}
Ответ №2:
Моя последняя попытка с FutureProvider, но автологин не вызывается: (
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:serialtrip/config/theme.dart';
import 'package:serialtrip/pages/home.dart';
import 'package:serialtrip/pages/login.dart';
import 'package:serialtrip/pages/splash.dart';
import 'package:serialtrip/providers/authProvider.dart';
main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AuthProvider(),
),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Serialtrip',
debugShowCheckedModeBanner: false,
theme: defaultTheme,
home: FutureProvider(
create: (_) => AuthProvider().autoLogin(),
initialData: Status.Authenticating,
child: _showScreen(context),
));
}
}
Widget _showScreen(BuildContext context) {
switch (context.watch<AuthProvider>().loggedInStatus) {
case Status.Authenticating:
return Splash();
case Status.LoggedIn:
return Home();
default:
return Login();
}
}
AuthProvider.dart
import 'package:flutter/widgets.dart';
enum Status { NotLoggedIn, LoggedIn, Authenticating, LoggedOut }
class AuthProvider with ChangeNotifier {
Status _loggedInStatus;
String _token;
Status get loggedInStatus => _loggedInStatus;
String get token => _token;
Future<String> autoLogin() async {
_loggedInStatus = Status.Authenticating;
notifyListeners();
print('autologin - waiting');
await new Future.delayed(const Duration(seconds: 2));
_token = 'abcd1234';
_loggedInStatus = Status.LoggedIn;
notifyListeners();
print('autologin - sucess');
return _token;
}
Future login(String email, String password) async {
_loggedInStatus = Status.Authenticating;
notifyListeners();
print('login - waiting');
await new Future.delayed(const Duration(seconds: 2));
_token = 'abcd1234';
_loggedInStatus = Status.LoggedIn;
notifyListeners();
print('login - sucess');
}
Future logout() async {
_token = null;
_loggedInStatus = Status.LoggedOut;
notifyListeners();
print('logout - sucess');
}
}
Комментарии:
1. Я вижу, вы не использовали
Consumer
forFutureProvier
.2. Я ответил на ваш вопрос по двум возможным сценариям.
Ответ №3:
Пытался использовать
context.read<AuthProvider>
внутри либоbuild
метод, либоupdate
обратный вызов поставщика
Если вы прочитаете документацию, вы заметите, что она не позволяет вам использовать контекст. прочитайте внутри метода сборки, используйте Provider.of<AuthProvider>(context, listen: false).autoLogin();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Serialtrip',
debugShowCheckedModeBanner: false,
theme: defaultTheme,
home: context.watch<AuthProvider>().loggedInStatus == Status.LoggedIn
? Home()
: FutureBuilder(
future: Provider.of<AuthProvider>(context, listen: false).autoLogin(),
builder: (ctx, authResultSnapshot) =>
authResultSnapshot.connectionState == ConnectionState.waiting ? CircularProgressIndicator() : Login(),
),
);
}
}
Комментарии:
1. Нет, этот метод дает тот же результат ошибки. Это похоже на метод context.read.
Ответ №4:
Первая возможность
Чтобы определить влияние autoLogin
функции, вам необходимо использовать Consumer
Widget _showScreen(BuildContext context) {
return Consumer<Status>(
builder: (context, loginStatus, child) {
switch (loginStatus) {
case Status.Authenticating:
return Splash();
case Status.LoggedIn:
return Home();
default:
return Login();
}
}
);
}
Обновите свою autoLogin
функцию следующим образом.
Future<Status> autoLogin() async {
await new Future.delayed(const Duration(seconds: 2));
_loggedInStatus = Status.LoggedIn;
return _loggedInStatus;
}
Вторая возможность (рекомендуется)
Если вы хотите непрерывно прослушивать изменения loggedInState, вам нужно StreamProvider
, чтобы since FutureProvier
прослушивал изменения только один раз.
В этом случае,
MyApp Class
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Serialtrip',
debugShowCheckedModeBanner: false,
theme: defaultTheme,
home: StreamProvider(
create: (_) => AuthProvider().autoLogin(),
initialData: Status.Authenticating,
child: _showScreen(context),
));
}
}
AutoLogin Stream
Вам нужно перейти AutoLogin
с future на stream
Stream<Status> autoLogin() async* {
_loggedInStatus = Status.Authenticating;
yield _loggedInStatus;
await new Future.delayed(const Duration(seconds: 2));
_loggedInStatus = Status.LoggedIn;
yield _loggedInStatus;
}
Consumer
остается неизменным.
Widget _showScreen(BuildContext context) {
return Consumer<Status>(
builder: (context, loginStatus, child) {
switch (loginStatus) {
case Status.Authenticating:
return Splash();
case Status.LoggedIn:
return Home();
default:
return Login();
}
}
);
}
Комментарии:
1.Вы можете обратиться к следующим репозиториям, чтобы получить представление о том, как
FutureProvider
иStreamProvider
как работать. FutureProvider gist.github.com/jtlapp/bbcc6d24f7f528ab9a651e775d8ad000 Поставщик потокового видео gist.github.com/jtlapp/0754502359435ca0de7eb25c39e0be702. я пробую второе решение. Я также изменил Future<Статус> (синтаксическая ошибка?) Но теперь выход из системы кажется не очень хорошим. LoginStatus не изменяется для перехода на страницу входа? Или мне нужно также изменить логин и выход из системы на stream, а не на future? потому что вы не используете context.read()
3. Вы где-нибудь храните loggeInState?
4. Поскольку это приложение не подключено к реальному бэкэнду, реализация такой функциональности во внешнем интерфейсе не звучит. Если вы все еще хотите это сделать, возможно, вы сможете найти способ его реализации, но это не кажется плодотворным. Я пытаюсь найти способ, хотя 🙂
5. на данный момент нет необходимости в бэкэнде. Я просто пытаюсь прослушать переменную _loggedInStatus. Когда я нажимаю на выход из системы, переменная изменяется, а уведомитель или поток отправляют значение newx, поэтому в основном я пытаюсь перейти на страницу входа в систему.