sqlite - 如何在Listview中恢复数据

标签 sqlite flutter

我的目标是将数据从 sql 数据库恢复到 Listview。首先,我开始创建数据库和模型类。其次我意识到我必须使用 FutureBuilder。但我不明白,如何在我的案例中使用这些东西。另外我知道,那还得用GlobalKey。

This is my code. In this version of my code, Alert dialog doesn't work

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:path/path.dart';
import 'dart:ui';
import 'package:samuraigym/program_training_handler.dart';
import 'package:samuraigym/my_icons_icons.dart' as custicon;
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'dart:io' as io;
import 'package:fluttertoast/fluttertoast.dart';
class MeasurementsScreen extends StatefulWidget {
  @override
  _MeasurementsScreenState createState() => _MeasurementsScreenState();
}

class _MeasurementsScreenState extends State<MeasurementsScreen> {
  List<ListItem> listItems;
  final scaffoldKey = new GlobalKey<ScaffoldState>();
  final formKey = new GlobalKey<FormState>();
  String typeOfMuscle;
  String numberOfMuscle;

  var nameItem = ["Рост","Вес","Шея","Плечевой пояс","Грудь","Бицепс",
    "Предплечье","Запястье","Живот","Бедро","Голень","Лодыжка"];
  @override
  void initState() {
    super.initState();
    initListItems();
  }

  void initListItems() {
    listItems = [
      new ListItem(
          detail: nameItem[0],
          index: 0,
          data: " "),
      new ListItem(
          detail: nameItem[1],
          index: 1,
          data: " "),
      new ListItem(
          detail: nameItem[2],
          index: 2,
          data: " "),
      new ListItem(
          detail: nameItem[3],
          index: 3,
          data: " "),
      new ListItem(
          detail: nameItem[4],
          index: 4,
          data: " "),
      new ListItem(
          detail: nameItem[5],
          index: 5,
          data: " "),
      new ListItem(
          detail: nameItem[6],
          index: 6,
          data: " "),
      new ListItem(
          detail: nameItem[7],
          index: 7,
          data: " "),
      new ListItem(
          detail: nameItem[8],
          index: 8,
          data: " "),
      new ListItem(
          detail: nameItem[9],
          index: 9,
          data: " "),
      new ListItem(
          detail: nameItem[10],
          index: 10,
          data: " "),
      new ListItem(
          detail: nameItem[11],
          index: 11,
          data: " ")


    ];
  }
  void sumbitContact(int index, String numberOfMuscle) {
    if(this.formKey.currentState.validate())
      formKey.currentState.save();
    else
      return null;

    var measurementsDatabaseModel = MeasurementsDatabaseModel();
    measurementsDatabaseModel.numberOfMuscle = numberOfMuscle;
    measurementsDatabaseModel.typeOfMuscle = index as String;

    var dbHelper = DatabaseHelperForMeasurements();
    dbHelper.addNewMeasurementsDatabaseModel(measurementsDatabaseModel);
    Fluttertoast.showToast(msg: 'Contact was saved',
        toastLength: Toast.LENGTH_SHORT);

  }
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      key: scaffoldKey,
      backgroundColor: Color(0xff2b2b2b),
      appBar: AppBar(
        backgroundColor: Colors.lightGreen[400],
        title: Text(
          'Замеры',
          style: new TextStyle(
            color: Colors.white
          ),),
        leading: IconButton(
          icon:Icon(Icons.arrow_back),
          color: Colors.white ,
          onPressed:() => Navigator.of(context).pop(),
        ),
      ),
      body: FutureBuilder<List<MeasurementsDatabaseModel>>(
        future: getMeasurementsDatabaseModelFromDB(),
        builder: (context, snapshot){
        if(snapshot.data != null  && snapshot.hasData){
          return  ListView.builder(
            physics: BouncingScrollPhysics(),
            itemCount: listItems.length,
            itemBuilder: (BuildContext ctxt, int index) => listItems[index],
          );
        } else {
          return ListView.builder(
            physics: BouncingScrollPhysics(),
            itemCount: listItems.length,
            itemBuilder: (BuildContext ctxt, int index) => listItems[index],
          );
        }
        }
      )
    );
  }



}

Future<List<MeasurementsDatabaseModel>> getMeasurementsDatabaseModelFromDB() async {
  var dbHelper = DatabaseHelperForMeasurements();
  Future<List<MeasurementsDatabaseModel>> contacts = dbHelper.getMeasurementsDatabaseModel();
  return contacts;
}

class ListItem extends StatefulWidget {
  String detail;
  int index;
  String data;
  _MeasurementsScreenState measurementsScreen;
  ListItem({Key key, this.detail, this.index, this.data}) : super(key: key);

  @override
  _ListItem createState() => _ListItem(measurementsScreen);
}
class _ListItem extends State<ListItem>  {
  bool isAppear = false;
  final _MeasurementsScreenState measurementsScreen;
  DatabaseHelperForMeasurements db  = DatabaseHelperForMeasurements();
  _ListItem(this.measurementsScreen);
  MeasurementsDatabaseModel measurementsDatabaseModel = new MeasurementsDatabaseModel();
  String typeOfMuscle;
  String numberOfMuscle;

  String lastSelectedValue;
  var name = ["Рост","Вес","Шея","Плечевой пояс","Грудь","Бицепс",
    "Предплечье","Запястье","Живот","Бедро","Голень","Лодыжка"];
  var indication = ["Ваш рост","Ваш вес","Ваша шея","Ваш плечевой пояс","Ваша грудь","Ваш бицепс",
    "Ваше предплечье","Ваше запястье","Ваш живот","Ваше бедро","Ваша голень","Ваша лодыжка"];
  var prefix = ["см: ","кг: ","см: ","см: ","см: ","см: ","см: ","см: ","см: ","см: ","см: ","см: "];
  var prefixAlert = ["см","кг","см","см","см","см","см","см","см","см","см","см"];
  TextEditingController customcintroller;


  Future<String> createAlertDialog(BuildContext context, int indexAl) async{
    customcintroller = TextEditingController();
      String returnVal = await showDialog(
          context: context, builder: (context){
        return AlertDialog(
          title: Text(name[indexAl]),
          content: TextFormField(
            textDirection: TextDirection.ltr,
            controller: customcintroller,
            style: TextStyle(
                color: Colors.lightGreen[400],
                fontSize: 18.5),
            decoration: InputDecoration(
              contentPadding: EdgeInsets.only(bottom: 4.0),
              labelText: indication[indexAl],
              suffixText: prefixAlert[widget.index],
              alignLabelWithHint: false,
            ),
            keyboardType: TextInputType.phone,
            textInputAction: TextInputAction.done,
            onSaved: (val) => this.numberOfMuscle = val,

          ),
          actions: <Widget>[
            FlatButton(
              child: const Text('ОТМЕНА'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
            FlatButton(
              child: const Text('ОК'),
              onPressed: () {
                setState(() {
                  widget.data = customcintroller.text.toString();
                  isAppear = !isAppear;
                  measurementsScreen.sumbitContact(widget.index, widget.data);
                  getMeasurementsDatabaseModelFromDB();
                  Navigator.of(context).pop();
                });
              },
            ),
          ],
        );
      });

      return returnVal;

  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: GestureDetector(
        onTap: () {
          createAlertDialog(context, widget.index);
        },
        child: Container(
          color: Color(0xff2b2b2b),
          height: 55.0,
          margin: const EdgeInsets.symmetric(
            vertical: 1.0,
          ),
          child: new Stack(
            children: <Widget>[
              new Container(
                child: new SizedBox.expand(
                  child: Container(
                    alignment: Alignment.center,
                    child: new Row(
                      crossAxisAlignment: CrossAxisAlignment.center,
                      mainAxisSize: MainAxisSize.max,
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: <Widget>[
//                    Padding(
//                      padding: const EdgeInsets.all(8.0),
//                    child: Icon(
//                      custicon.MyIcons.bathroom_scale,
//                      color: Colors.lightGreen[400],
//                      size: 40.0)),
                        Padding(
                          padding: const EdgeInsets.all(16.0),
                          child: new Text(
                            widget.detail,
                            style:
                            new TextStyle(fontSize: 16.0, color: Colors.white),
                          ),
                        ),
                        Container(
                          alignment: Alignment.centerRight,
                          child: isAppear ? Padding(
                              padding: EdgeInsets.all(8.0),
                              child: Container(
                                  decoration: ShapeDecoration(
                                    color: Colors.lightGreen[400],
                                    shape: RoundedRectangleBorder(
                                      side: BorderSide(width: 1.0, style: BorderStyle.solid, color: Colors.white),
                                      borderRadius: BorderRadius.all(Radius.circular(5.0)),
                                    ),
                                  ),
                                  child: Padding(
                                      padding: EdgeInsets.all(4.0),
                                      child: new Text(
                                        prefix[widget.index] + widget.data,
                                        style: new TextStyle(
                                            fontSize: 16.0,
                                            color: Colors.white
                                        ),
                                      )))) : SizedBox(),
                        )
                      ],
                    ),
                  ),
                ),
              ),
            ],
          ),
        )));
  }
}
class MeasurementsDatabaseModel{
  int id;
  String typeOfMuscle;
  String numberOfMuscle;
  MeasurementsDatabaseModel();
}
class DatabaseHelperForMeasurements{
  static Database db_instance;
  final String TABLE_NAME = "Measurements";

  Future<Database> get db async{
    if(db_instance == null)
      db_instance = await initDB();
    return db_instance;
  }
  initDB() async {
    io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path,"Measurements_db.db");
    var db = await openDatabase(path,version: 1, onCreate: onCreateFunc);
    return db;
  }

  void onCreateFunc(Database db, int version) async{

    await db.execute('CREATE TABLE $TABLE_NAME(id INTEGER PRIMARY KEY AUTOINCREMENT, typeOfMuscle TEXT, numberOfMuscle TEXT);');

  }

  Future<List<MeasurementsDatabaseModel>> getMeasurementsDatabaseModel() async{
    var db_connection = await db;
    List<Map> list = await db_connection.rawQuery('SELECT * FROM $TABLE_NAME');
    List<MeasurementsDatabaseModel> modelList = new List();
    for(int i = 0; i < list.length; i++){
      MeasurementsDatabaseModel measurementsDatabaseModel = new MeasurementsDatabaseModel();
      measurementsDatabaseModel.id = list[i]['id'];
      measurementsDatabaseModel.typeOfMuscle = list[i]['typeOfMuscle'];
      measurementsDatabaseModel.numberOfMuscle = list[i]['numberOfMuscle'];

      modelList.add(measurementsDatabaseModel);
    }
    return modelList;
  }

  void addNewMeasurementsDatabaseModel(MeasurementsDatabaseModel measurementsDatabaseModel) async {
    var db_connection = await db;
    String query =
        'INSERT INTO $TABLE_NAME(name,phone) VALUES( \'${measurementsDatabaseModel.typeOfMuscle}\',\'${measurementsDatabaseModel.numberOfMuscle}\')';
    await db_connection.transaction((transition) async{
      return await transition.rawInsert(query);
    });
  }
  void updateMeasurementsDatabaseModel(MeasurementsDatabaseModel measurementsDatabaseModel) async {
    var db_connection = await db;
    String query =
        'UPDATE $TABLE_NAME SET name =\'${measurementsDatabaseModel.typeOfMuscle}\',phone =\'${measurementsDatabaseModel.typeOfMuscle}\' WHERE id =${measurementsDatabaseModel.id}';
    await db_connection.transaction((transition) async{
      return await transition.rawQuery(query);
    });
  }
  void deleteMeasurementsDatabaseModel(MeasurementsDatabaseModel measurementsDatabaseModel) async {
    var db_connection = await db;
    String query = 'DELETE FROM $TABLE_NAME WHERE id = ${measurementsDatabaseModel.id}';
    await db_connection.transaction((transition) async{
      return await transition.rawQuery(query);
    });
  }
}

这是gif,我要保存数据的地方。您怎么看,屏幕右侧附近有一个带有文本的容器。在那里我想保存数据,我把它放在 AlertDialog 中。

enter image description here

最佳答案

首先,我们将讨论其简化的实现。在主屏幕上, 我们不会将它连接到 SQLite。但稍后,我们可以讨论更复杂的 通过调用 SQLite 查询实现。

我们应该使用 FutureBuilder 吗?

在互联网上传播的许多教程中,应用程序屏幕可以显示正在加载指示器,随后,它会显示 ListView 及其 ListTile 在获取到数据库后。

如果稍后没有与 ListView 的进一步交互,它会很好地工作。

当我们使用 future 时,构建器只会触发两次

class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: FutureBuilder(
        future: queryDatabase(), // Calling Database
        builder: (context, snapshot) {
          if (snapshot.hasData) { // After Callback, it may triggers this Part
            return ListView.builder(
              itemBuilder: (_, index) {
                return ListTile(
                  title: Text("$index"),
                );
              },
            );
          }
          return Center( // First Triggering this Part
            child: CircularProgressIndicator(),
          );
        },
      ),
    );
  }
}

解决方案是Stateful Widget!

通过使用 Stateful Widget,我们可以将我们的产品存储在变量中。所以, 每次调用 MainScreen 的构建方法时,应用程序都会显示更新的列表。

class MainScreen extends StatefulWidget {
  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  List<Map<String, dynamic>> products = []; // Store Item, Here

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(...),
      body: Container(
        child: renderProducts(), // Render stored Item, here
      ),
    );
  }

我们可以在下面的演示中看到,当我们与按钮交互时,构建方法 总是被召回幸运的是,我们可以得到我们预期的屏幕

Demo

确定的问题

class _ListItem extends State<ListItem>  {
  String typeOfMuscle;
  String numberOfMuscle;
  TextEditingController customcintroller;
  ...

  AlertDialog(
    title: Text(name[indexAl]),
    content: TextFormField(
      controller: customcintroller,
      onSaved: (val) => this.numberOfMuscle = val,
    ),
  );
),

问题是,在 onSaved 方法中,应用程序只会更新局部变量 驻留在每个 ListItem 上。因此,应用程序不知道新值 应该显示。

如何让小部件重新渲染

通过使用 setState((){}) 作为下面的代码

class _ListItem extends State<ListItem>  {
  String typeOfMuscle;
  String numberOfMuscle;
  TextEditingController customcintroller;
  ...

  void updateAndRerender(val){
    this.numberOfMuscle = val;
    setState((){});
  }

  AlertDialog(
    title: Text(name[indexAl]),
    content: TextFormField(
      controller: customcintroller,
      onSaved: (val) {
        updateAndRerender(val);
      },
    ),
  );
),

工作示例-应用 repo

您可以查看此存储库。 Github

关于sqlite - 如何在Listview中恢复数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57544930/

相关文章:

php - 如何使用 SQLite 和 PHP 将图像保存到数据库?

node.js - 启动新的 Sequelize 后端但无法连接到 SQLite DB

sql - 使用SQLite在SQL中进行关系划分

android - Android依赖项 'com.google.firebase:firebase-iid'对于编译(19.0.0)和运行时(20.0.1)类路径具有不同的版本

flutter - Flutter的sqflite查询

php - 指向数据库的符号链接(symbolic link)在/var/www 中不起作用

java - 如何在 Android 中使用 SQLite 多表触发带有计数的连接查询?

flutter - 我可以检查字符串是否包含表情符号吗?

flutter - 阻止动画立即播放

flutter - 删除 flutter 中的日期