通过 RestApi 在 Login 中构建 Consumer 抛出以下 StackOverflowError

The following StackOverflowError was thrown building Consumer in Login through RestApi

我正在使用 Rest Api 构建简单的身份验证。功能正在运行,但是当我获得令牌时,我想在应用程序的 mainPage 上移动。为此,我使用 Consumer 使用我从 _auth.token 获得的 bool 值构建主页,当令牌存在时它返回 Stack Overflow 错误。

The following WhosebugError was thrown building Consumer<Auth>(dirty, dependencies:
[_InheritedProviderScope<Auth>]):
Stack Overflow
The relevant error-causing widget was:
  Consumer<Auth>

我的代码是 main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:sole_entrepreneur/screens/business_overview_screen.dart';
import 'package:sole_entrepreneur/screens/splash_screen.dart';

// import './screens/selection_screen.dart';
import './screens/login_screen.dart';
import './screens/register_screen.dart';
import './screens/selection_screen.dart';
import './providers/auth.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
  // await Auth().signIn('kaleem.excel@gmail.com', 'Developer');
  // await Auth().register(
  //     'shary101@gmail.com', "csdwx9spq", "matshary", "test", "testlast");
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (ctx) => Auth(),
          ),
        ],
        child: Consumer<Auth>(builder: (ctx, auth, _) {
          // print(auth.isAuth);
          return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              scaffoldBackgroundColor: Colors.white,
              primaryColor: Color.fromRGBO(124, 116, 146, 1),
              buttonTheme: ButtonThemeData(textTheme: ButtonTextTheme.primary),
              appBarTheme: AppBarTheme(
                  color: Colors.white,
                  elevation: 0.0,
                  iconTheme: IconThemeData(color: Colors.black)),
            ),
            home: auth.isAuth
                ? BusinessOverViewScreen()
                : FutureBuilder(
                    builder: (context, authResultSnapshot) {
                      print(auth.isAuth);
                      return authResultSnapshot.connectionState ==
                              ConnectionState.waiting
                          ? SplashScreen()
                          : LoginScreen();
                    },
                    future: auth.tryAutoLogin(),
                  ),
            routes: {
              LoginScreen.routeName: (ctx) => LoginScreen(),
              RegisterScreen.routeName: (ctx) => RegisterScreen(),
              BusinessOverViewScreen.routeName: (ctx) =>
                  BusinessOverViewScreen()
            },
          );
        }));
  }
}

auth.dart

import 'dart:convert';
import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

class Auth with ChangeNotifier {
  String _token;
  DateTime _expiryDate;
  int _userId;
  Timer _authTimer;
  List<dynamic> _userRole;
  String _userEmail;
  String _userNiceName;
  String _userDisplayName;
  String _userAvatar;
  String message;

  bool get isAuth {
    return token != null;
  }

  String get token {
    print(_expiryDate);
    // print(_expiryDate.isAfter(DateTime.now()).toString());

    if (_expiryDate != null &&
        _expiryDate.isAfter(DateTime.now()) &&
        _token != null) {
      return token;
    }
    return null;
  }

  int get userId {
    return _userId;
  }

  Future<void> authethicate(
    String email,
    String password,
  ) async {
    try {
      final url = 'https://soleentrepreneur.co.uk/wp-json/jwt-auth/v1/token';
      final response = await http.post(
        url,
        body: {
          'username': email,
          'password': password,
        },
      );
      print(
        json.decode(response.body),
      );
      final responseData = json.decode(response.body);
      if (responseData['message'] != null) {
        throw HttpException(responseData['message']);
      }
      _token = responseData['token'];
      _userEmail = responseData['user_email'];
      _userNiceName = responseData['user_nicename'];
      _userDisplayName = responseData['user_display_name'];
      _userRole = responseData['user_role'];
      _userId = responseData['user_id'];
      _userAvatar = responseData['avatar'];
      _expiryDate = DateTime.now().add(new Duration(days: 7));
      notifyListeners();
      SharedPreferences pref = await SharedPreferences.getInstance();
      final userData = json.encode({
        'token': _token,
        'userId': _userId,
        'expiryDate': _expiryDate.toIso8601String(),
        'userEmail': _userEmail,
        'userNiceName': _userNiceName,
        'userDisplayName': _userDisplayName,
        'userRole': _userRole,
        'userAvatar': _userAvatar,
      });
      pref.setString('userData', userData);
    } catch (error) {
      throw (error);
    }
  }

  Future<void> signIn(String email, String password) async {
    await authethicate(email, password);
  }

  Future<void> register(String email, String password, String username,
      String firstName, String lastName) async {
    await registerAuth(email, username, password, firstName, lastName);
  }

  Future<void> registerAuth(String email, String username, String password,
      String firstName, String lastName) async {
    try {
      final registerUrl =
          'https://soleentrepreneur.co.uk/wp-json/wp/v2/users/register';
      final response = await http.post(registerUrl,
          headers: {'Content-Type': 'application/json'},
          body: json.encode(
            {
              'username': username,
              'password': password,
              'email': email,
              'first_name': firstName,
              'last_name': lastName
            },
          ));

      final responseData = json.decode(response.body);

      if (responseData['code'] != 200) {
        throw HttpException(responseData['message']);
      }
      print(
        json.decode(response.body),
      );
      message = responseData['message'];
      notifyListeners();
    } catch (error) {
      throw error;
    }
  }

  Future<void> logOut() async {
    _userId = null;
    _token = null;
    _expiryDate = null;
    if (_authTimer != null) {
      _authTimer.cancel();
      _authTimer = null;
    }
    notifyListeners();
    final prefs = await SharedPreferences.getInstance();
    prefs.clear();
  }

  Future<void> tryAutoLogin() async {
    final prefs = await SharedPreferences.getInstance();
    if (!prefs.containsKey('userData')) {
      print(_token);
      return false;
    }
    final extractedUserData =
        json.decode(prefs.getString('userData')) as Map<String, Object>;
    final expiryDate = DateTime.parse(extractedUserData['expiryDate']);
    if (expiryDate.isBefore(DateTime.now())) {
      print(_token);
      return false;
    }
    _token = extractedUserData['token'];
    _userId = extractedUserData['userId'];
    _expiryDate = expiryDate;
    _userEmail = extractedUserData['user_email'];
    _userDisplayName = extractedUserData['user_display_name'];
    _userNiceName = extractedUserData['user_nicename'];
    _userRole = extractedUserData['user_role'];
    _userAvatar = extractedUserData['avatar'];
    notifyListeners();
    _autoLogOut();
    print(_token);
    return true;
  }

  void _autoLogOut() {
    if (_authTimer != null) {
      _authTimer.cancel();
    }
    final timeToExpiry = _expiryDate.difference(DateTime.now()).inSeconds;
    _authTimer = Timer(Duration(seconds: timeToExpiry), logOut);
  }
}

login.dart

import 'dart:io';

import 'package:flutter/material.dart';
import '../providers/auth.dart';
import '../widgets/button.dart';
import '../widgets/text_form_field_style.dart';

import 'package:provider/provider.dart';
import '../models/httpException.dart';

class LoginScreen extends StatefulWidget {
  static const routeName = '/login';
  @override
  _LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final GlobalKey<FormState> _formKey = GlobalKey();
  Map<String, String> _authData = {
    'email': '',
    'password': '',
  };
  var _isLoading = false;
  void _showErrorDialog(String message) {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          content: Text(message),
          title: Text('An Error Occured'),
          actions: [
            FlatButton(
                onPressed: () => Navigator.of(context).pop(),
                child: Text('Okay'))
          ],
        );
      },
    );
  }

  Future<void> _submit() async {
    if (!_formKey.currentState.validate()) {
      print('not Returning');
      return;
    }
    _formKey.currentState.save();
    setState(() {
      _isLoading = true;
    });
    try {
      await Provider.of<Auth>(context, listen: false)
          .signIn(_authData['email'], _authData['password']);
    } on HttpException catch (error) {
      var errormessage = 'Authethication failed';
      if (error.toString().contains('Unknown email address.')) {
        errormessage =
            'Unknown email address. Check again or try your username';
      } else if (error.toString().contains('[jwt_auth] incorrect_password')) {
        errormessage =
            'The password you entered for the email address  is incorrect.';
      } else if (error.toString().contains('[jwt_auth] invalid_username')) {
        errormessage = 'This email is not valid';
      }
      _showErrorDialog(errormessage);
    } catch (error) {
      var errormessage = 'Could not authenticate you. Please try again later';
      print(error);
      _showErrorDialog(errormessage);
    }
    setState(() {
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: Column(
          children: [
            Expanded(
              child: Container(
                margin: EdgeInsets.all(MediaQuery.of(context).size.width / 5),
                child: Image.asset('assets/Logo.png'),
                color: Colors.blue,
              ),
            ),
            Expanded(
              child: Form(
                key: _formKey,
                child: SingleChildScrollView(
                  child: Column(
                    children: [
                      buildTextFormStyle(
                        MediaQuery.of(context).size.height / 9,
                        TextFormField(
                          decoration: InputDecoration(
                            labelText: 'Email',
                            border: InputBorder.none,
                            contentPadding: EdgeInsets.all(5),
                          ),
                          keyboardType: TextInputType.emailAddress,
                          // ignore: missing_return
                          validator: (value) {
                            if (value.isEmpty || !value.contains('@'))
                              return 'Invalid email!';
                          },
                          onSaved: (newValue) {
                            _authData['email'] = newValue;
                          },
                        ),
                      ),
                      SizedBox(height: 10),
                      buildTextFormStyle(
                        MediaQuery.of(context).size.height / 9,
                        TextFormField(
                          decoration: InputDecoration(
                            labelText: 'Password',
                            border: InputBorder.none,
                            contentPadding: EdgeInsets.all(5),
                          ),
                          obscureText: true,
                          // ignore: missing_return
                          validator: (value) {
                            if (value.isEmpty || value.length < 5)
                              return 'Password is too short!';
                          },
                          onSaved: (newValue) {
                            _authData['password'] = newValue;
                          },
                        ),
                      ),
                      if (_isLoading)
                        CircularProgressIndicator()
                      else
                        AppButton(name: 'LOGIN', onPressed: _submit),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

//Text Field Style

create: (ctx) => Auth(),

这个ctx不是页面的当前上下文你必须创建一个BuildContext的对象或者整个widget树的上下文是相同的所以你可以直接传递上下文

create: (context) => Auth(), 要么 create: (_) => Auth(),

错误是由于 Auth 的 getter 方法造成的。我 returning getter token 但我必须 return _token 字符串,这是我在将 in.Due 登录到 Name Confusion 后得到的return一遍又一遍getter。