我的目标是将数据从 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 中。
最佳答案
首先,我们将讨论其简化的实现。在主屏幕上, 我们不会将它连接到 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
),
);
}
我们可以在下面的演示中看到,当我们与按钮交互时,构建方法 总是被召回,幸运的是,我们可以得到我们预期的屏幕
确定的问题
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/