feat(database): 实现物品添加和列表展示功能

- 新增数据库相关代码,包括数据库帮助类和操作函数
- 重构添加物品页面,增加表单验证和数据提交逻辑
- 新增物品列表页面,实现数据加载和展示功能
- 更新项目配置,添加必要的依赖库
This commit is contained in:
LingandRX 2025-04-24 21:26:06 +08:00
parent 6c64c86239
commit 90c1d5238b
16 changed files with 418 additions and 71 deletions

View File

@ -28,6 +28,9 @@ android {
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
}
}
buildTypes {

View File

@ -2,6 +2,9 @@ allprojects {
repositories {
google()
mavenCentral()
// maven { url 'https://maven.aliyun.com/repository/google' }
// maven { url 'https://maven.aliyun.com/repository/jcenter' }
// maven { url 'https://maven.aliyun.com/repository/public' }
}
}
@ -15,4 +18,4 @@ subprojects {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
}

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
distributionUrl=file:///C:/Users/TxT/Downloads/gradle-8.3-all.zip

View File

@ -12,13 +12,15 @@ pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
// maven { url 'https://maven.aliyun.com/repository/google' }
// maven { url 'https://maven.aliyun.com/repository/jcenter' }
// maven { url 'https://maven.aliyun.com/repository/public' }
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.0" apply false
id "com.android.application" version "8.2.2" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
}

View File

@ -0,0 +1,47 @@
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static final _databaseName = "my_database.db";
static final _databaseVersion = 1;
static final table = 'items';
static final columnId = '_id';
static final columnName = 'name';
static final columnContext = 'context';
static final DatabaseHelper _instance = DatabaseHelper._internal();
factory DatabaseHelper() => _instance;
static Database? _database;
DatabaseHelper._internal();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
print('初始化成功!');
return _database!;
}
Future<Database> _initDatabase() async {
final directory = await getApplicationDocumentsDirectory();
final path = join(directory.path, _databaseName);
print('${path}');
return await openDatabase(
path,
version: _databaseVersion,
onCreate: _onCreate,
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT NOT NULL,
$columnContext
)
''');
}
}

View File

@ -0,0 +1,21 @@
import 'sqlite_helper.dart';
Future<int> insertItem(String name, String context) async {
final db = await DatabaseHelper().database;
final res = await db.insert(
DatabaseHelper.table,
{
DatabaseHelper.columnName: name,
DatabaseHelper.columnContext: context,
},
);
print('添加数据成功');
return res;
}
Future<List<Map<String, dynamic>>> getAllItems() async {
final db = await DatabaseHelper().database;
final res = await db.query(DatabaseHelper.table, orderBy: '_id DESC');
print('查询数据完成');
return res;
}

View File

@ -1,7 +1,11 @@
import 'package:flutter/material.dart';
import 'package:item_tracker/screens/home_screen.dart';
import 'database/sqlite_helper.dart';
void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await DatabaseHelper().database;
print('数据库初始化完成');
runApp(MyApp());
}

View File

@ -1,64 +1,91 @@
import 'dart:io'; // dart:io 使 File
import 'package:flutter/material.dart';
import 'package:item_tracker/models/item_model.dart'; // item_model
import 'package:item_tracker/database/sqlite_operation.dart';
class AddItemScreen extends StatelessWidget {
final TextEditingController _nameController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
String? _imagePath;
class AddItemScreen extends StatefulWidget {
@override
_FromTestRouteSate createState() => _FromTestRouteSate();
}
class _FromTestRouteSate extends State<AddItemScreen> {
TextEditingController _nameController = TextEditingController();
TextEditingController _contentController = TextEditingController();
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('添加物品'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _nameController,
decoration: InputDecoration(labelText: '名称'),
),
SizedBox(height: 16),
TextField(
controller: _descriptionController,
decoration: InputDecoration(labelText: '简介'),
maxLines: 3,
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
//
// 使 image_picker
// ImagePicker().pickImage(source: ImageSource.gallery);
},
child: Text('选择图片'),
),
if (_imagePath != null)
Image.file(File(_imagePath!)), // 使 File
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
// Item
Item item = Item(
name: _nameController.text,
description: _descriptionController.text,
imagePath: _imagePath,
);
//
//
//
print('名称: ${item.name}');
print('简介: ${item.description}');
print('图片路径: ${item.imagePath}');
},
child: Text('提交'),
),
],
),
title: Text('新增物品'),
),
body: Builder(builder: (context) {
return Form(
key: _formKey, // globalKey FormState
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
children: <Widget>[
TextFormField(
autofocus: true,
controller: _nameController,
decoration: InputDecoration(
labelText: "名称",
hintText: "请输入物品名称",
icon: Icon(Icons.person),
),
maxLength: 20,
//
validator: (v) {
if (v == null || v.trim().isEmpty) {
print('名称不能为空');
return "物品名称不能为空";
}
return null;
},
),
TextFormField(
controller: _contentController,
decoration: InputDecoration(
labelText: "物品描述",
hintText: "请输入物品描述",
icon: Icon(Icons.lock),
),
maxLength: 200,
),
//
Padding(
padding: const EdgeInsets.only(top: 28.0),
child: Row(
children: <Widget>[
Expanded(
child: ElevatedButton(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text("提交"),
),
onPressed: () {
if (_formKey.currentState!.validate()) {
//
print('物品名称:${_nameController.text}');
print('物品描述:${_contentController.text}');
insertItem(_nameController.text, _contentController.text);
//
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('提交成功'),
duration: Duration(seconds: 1),
));
//
Navigator.pop(context, true);
}
},
),
),
],
),
)
],
),
);
}),
);
}
}
}

View File

@ -1,26 +1,78 @@
import 'package:flutter/material.dart';
import 'package:item_tracker/screens/addItem/add_item_screen.dart';
class ItemListScreen extends StatelessWidget {
import '../database/sqlite_operation.dart';
class ItemListScreen extends StatefulWidget {
@override
_ItemListScreenState createState() => _ItemListScreenState();
}
class _ItemListScreenState extends State<ItemListScreen> {
late Future<List<Map<String, dynamic>>> _itemsFuture;
@override
void initState() {
super.initState();
_itemsFuture = getAllItems(); // Future
}
Future<void> _refreshData() async {
setState(() {
_itemsFuture = getAllItems(); // Future FutureBuilder
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('物品列表'),
),
body: Center(
child: Text('这里将显示物品列表'),
body: RefreshIndicator(
child: FutureBuilder<List<Map<String, dynamic>>>(
future: _itemsFuture, // Future
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else {
final items = snapshot.data ?? [];
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ListTile(
title: Text(item['name']),
subtitle: Text(item['context']),
);
},
);
}
},
),
onRefresh: _refreshData,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
//
Navigator.push(
onPressed: () async {
final shouldRefresh = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddItemScreen()),
);
print('shouldRefresh${shouldRefresh}');
if (shouldRefresh == true) {
print('刷新中');
_refreshData();
}
},
child: Icon(Icons.add, color: Colors.white,),
child: Icon(
Icons.add,
color: Colors.white,
),
),
);
}
}
}

View File

@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h"
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
}

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
sqlite3_flutter_libs
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -5,6 +5,12 @@
import FlutterMacOS
import Foundation
import path_provider_foundation
import sqflite_darwin
import sqlite3_flutter_libs
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
}

View File

@ -57,6 +57,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
flutter:
dependency: "direct main"
description: flutter
@ -116,13 +124,77 @@ packages:
source: hosted
version: "1.15.0"
path:
dependency: transitive
dependency: "direct main"
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.3.0"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
sky_engine:
dependency: transitive
description: flutter
@ -136,6 +208,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sqflite:
dependency: "direct main"
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
sqflite_common_ffi:
dependency: "direct main"
description:
name: sqflite_common_ffi
sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
url: "https://pub.dev"
source: hosted
version: "2.3.4+4"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
url: "https://pub.dev"
source: hosted
version: "2.4.1+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: "310af39c40dd0bb2058538333c9d9840a2725ae0b9f77e4fd09ad6696aa8f66e"
url: "https://pub.dev"
source: hosted
version: "2.7.5"
sqlite3_flutter_libs:
dependency: "direct main"
description:
name: sqlite3_flutter_libs
sha256: "1a96b59227828d9eb1463191d684b37a27d66ee5ed7597fcf42eee6452c88a14"
url: "https://pub.dev"
source: hosted
version: "0.5.32"
stack_trace:
dependency: transitive
description:
@ -160,6 +296,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
@ -176,6 +320,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.3"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:
@ -192,6 +344,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.3.0"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
sdks:
dart: ">=3.4.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"

View File

@ -11,9 +11,14 @@ environment:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
sqflite: ^2.3.0
path: ^1.8.0
sqflite_common_ffi: ^2.3.0
sqlite3_flutter_libs: ^0.5.32
path_provider: ^2.0.2
dev_dependencies:
flutter_test:
sdk: flutter

View File

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h"
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
}

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
sqlite3_flutter_libs
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST