简单的Flutter sqflite登录->写入数据库->导航->从数据库流中检索

时间:2019-03-10 14:09:18

标签: sqlite flutter async-await

处理此问题的正确方法是什么,我已经进行了很多搜索,并且大多数使用将来的构建器的示例都使用它们来绘制列表,所以也许我应该在这里避免全部使用它们。

我想在登录时(如果成功浏览到主页)提交登录表单,执行网络请求并绘制进度栏。如果不成功,则应终止进度条并重新绘制主页。该部分似乎正常工作,不确定我是否正确使用了导航器。

登录调用返回用户和访问令牌对象。主页需要检索成功登录响应写入数据库的访问令牌。据我所知,导航进行得太快了,访问令牌的检索似乎发生在导航至主页之前。

class LoginPage extends StatefulWidget {
  LoginPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {

  bool _isValidForm = true;

  Future<LoginResponse> _user;

  void _submitLogin() {
    setState(() {
      if (_isValidForm) {
        _user = login().then((_) => Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage())));
      }
    });
  }

  Widget _buildLoginForm(AsyncSnapshot<LoginResponse> snapshot) {
    if (snapshot.connectionState != ConnectionState.none && !snapshot.hasData) {
      return new Center(child: new CircularProgressIndicator());
    } else {
      return SafeArea(
        child: Center(
          child: new ListView(
            children: <Widget>[
              //..more views
              Padding(
                padding: EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 16.0),
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      //..email and password fields
                      FlatButton(
                          child: new Text(
                            'SIGN IN',
                          ),
                          onPressed: _submitLogin),
                    ]),
              )
            ],
          ),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return new FutureBuilder(
      future: _user,
      builder: (context, AsyncSnapshot<LoginResponse> snapshot) {
        return new Scaffold(
          backgroundColor: kMyGreen,
          body: _buildLoginForm(snapshot),
        );
      },
    );
  }

  Future<LoginResponse> login() async {
    final response = await http.post(...);

    if (response.statusCode == 200) {
      var loginResponse = LoginResponse.fromJson(json.decode(response.body));

      //Write the user details to local db
      DBProvider.db.newUser(loginResponse.user);
      //Write the tokens to local db
      DBProvider.db.newToken(loginResponse.tokens);

      return loginResponse;
    } else {
      throw Exception('Failed to login');
    }
  }

}

数据库方法:

  newUser(User newUser) async {
    final db = await database;
    //get the biggest id in the table
    var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM User");
    int id = table.first["id"];
    //insert to the table using the new id
    var raw = await db.rawInsert(
        "INSERT Into User (id,first_name,last_name)"
        " VALUES (?,?,?)",
        [id, newUser.firstName, newUser.lastName]);
    return raw;
  }

  newToken(Tokens newTokens) async {
    final db = await database;
    //await db.rawDelete("DELETE FROM Token");
    //get the biggest id in the table
    var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM Token");
    int id = table.first["id"];
    //insert to the table using the new id
    var raw = await db.rawInsert(
        "INSERT Into Token (id,access_token,refresh_token)"
        " VALUES (?,?,?)",
        [id, newTokens.accessToken, newTokens.refreshToken]);
    return raw;
  }

  Future<Tokens> getToken() async {
    final db = await database;
    var res = await db.query("Token", limit: 1);
    return res.isNotEmpty ? Tokens.fromJson(res.first) : null;
  }

主页

class HomePage extends StatefulWidget {
  HomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _HomePageState createState() => _HomePageState();

}

class _HomePageState extends State<HomePage>{

  @override
  void initState() {
    super.initState();
    getHomePageStuff();
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home Page"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {},
          child: Text('Go back!'),
        ),
      ),
    );
  }
}


Future<HomePageStuffResponse> getHomePageStuff() async {
  Tokens token = await DBProvider.db.getToken();

  //Accessing the token here throws an NPE
  var accessToken = token.accessToken;
  debugPrint("token = " + accessToken);

  final response = await http.get(..);

  if (response.statusCode == 200) {
    debugPrint("FETCH SUCCESS");
    return stuff;
  } else {
    throw Exception('Failed to fetch home page stuff');
  }
}

2 个答案:

答案 0 :(得分:1)

您可以像这样将angular.json主体包裹在Scaffold's

FutureBuilder

答案 1 :(得分:0)

好的,我离得很近。导航是一种很好的方式,问题在于未await写入数据库,因此导航会同时发生(newUsernewToken调用)。当我导航到主屏幕并尝试读取访问令牌时,呼叫将失败,因为该呼叫尚不存在。

很难弄清这一点,因为调试器在Android Studio中有些混乱,因此我只需要将所有内容都记录到控制台即可查看问题。

如果您阅读我的问题,谢谢您的时间:)