Невозможно использовать provider и FutureBuilder?

#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 for FutureProvier .

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/0754502359435ca0de7eb25c39e0be70

2. я пробую второе решение. Я также изменил Future<Статус> (синтаксическая ошибка?) Но теперь выход из системы кажется не очень хорошим. LoginStatus не изменяется для перехода на страницу входа? Или мне нужно также изменить логин и выход из системы на stream, а не на future? потому что вы не используете context.read()

3. Вы где-нибудь храните loggeInState?

4. Поскольку это приложение не подключено к реальному бэкэнду, реализация такой функциональности во внешнем интерфейсе не звучит. Если вы все еще хотите это сделать, возможно, вы сможете найти способ его реализации, но это не кажется плодотворным. Я пытаюсь найти способ, хотя 🙂

5. на данный момент нет необходимости в бэкэнде. Я просто пытаюсь прослушать переменную _loggedInStatus. Когда я нажимаю на выход из системы, переменная изменяется, а уведомитель или поток отправляют значение newx, поэтому в основном я пытаюсь перейти на страницу входа в систему.