feat(category): 新增分类功能并重构相关屏幕

- 新增 CategoryScreen 屏幕用于显示分类- 新增 ItemCategory 模型类用于分类数据管理
- 新增 ItemCategoryRepository 用于分类数据持久化
- 新增 item_category_table 创建分类表结构
-重构 HomeScreen 底部导航栏,增加分类选项
- 重命名相关屏幕文件,统一命名规范
- 调整 ItemScreen 以适应新增的分类功能- 更新 SQLiteHelper 以支持分类表创建和默认分类插入
- 删除 StatisticsScreen 屏幕
This commit is contained in:
LingandRX 2025-05-07 22:52:20 +08:00
parent b7d1cdc62e
commit b2217ae2be
12 changed files with 214 additions and 54 deletions

View File

@ -0,0 +1,13 @@
const String createItemCategoryTable = '''
CREATE TABLE item_category (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
''';
const String insertDefaultCategory = '''
INSERT INTO item_category (name, description) VALUES ('默认分类', '系统默认分类');
''';

View File

@ -3,6 +3,8 @@ import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'item_category_table.dart';
class DatabaseHelper {
//
static final _databaseName = "my_database.db";
@ -35,5 +37,7 @@ class DatabaseHelper {
Future<void> _onCreate(Database db, int version) async {
await db.execute(createItemTable);
await db.execute(createItemCategoryTable);
await db.execute(insertDefaultCategory);
}
}

View File

@ -0,0 +1,54 @@
class ItemCategory {
final int? id;
final String name;
final String? description;
final String? createdAt;
final String? updatedAt;
ItemCategory({
this.id,
required this.name,
this.description,
this.createdAt,
this.updatedAt,
});
// fromMap map -> Dart
factory ItemCategory.fromMap(Map<String, dynamic> map) {
return ItemCategory(
id: map['id'] as int?,
name: map['name'] as String,
description: map['description'] as String?,
createdAt: map['created_at'] as String?,
updatedAt: map['updated_at'] as String?,
);
}
// toMap Dart -> map
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'description': description,
'created_at': createdAt,
'updated_at': updatedAt,
};
}
// copyWith 便
ItemCategory copyWith({
int? id,
String? name,
String? description,
String? createdAt,
String? updatedAt,
}) {
return ItemCategory(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}

View File

@ -1,4 +1,4 @@
import 'package:item_tracker/screens/item_screens/add_item_screen.dart';
import 'package:item_tracker/screens/item_screens/item_screen.dart';
class Item {
int? id;

View File

@ -0,0 +1,57 @@
import 'package:sqflite/sqflite.dart';
import '../models/item_category_model.dart';
class ItemCategoryRepository {
final Database db;
ItemCategoryRepository(this.db);
// (Create)
Future<int> insert(ItemCategory category) async {
return await db.insert(
'item_category',
category.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// (Read All)
Future<List<ItemCategory>> getAll() async {
final List<Map<String, dynamic>> maps = await db.query('item_category');
return maps.map((map) => ItemCategory.fromMap(map)).toList();
}
// (Read One by id)
Future<ItemCategory?> getById(int id) async {
final List<Map<String, dynamic>> maps = await db.query(
'item_category',
where: 'id = ?',
whereArgs: [id],
limit: 1,
);
if (maps.isNotEmpty) {
return ItemCategory.fromMap(maps.first);
}
return null;
}
// (Update)
Future<int> update(ItemCategory category) async {
return await db.update(
'item_category',
category.toMap(),
where: 'id = ?',
whereArgs: [category.id],
);
}
// (Delete)
Future<int> delete(int id) async {
return await db.delete(
'item_category',
where: 'id = ?',
whereArgs: [id],
);
}
}

View File

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
class CategoryScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
const items = 20;
return Scaffold(
appBar: AppBar(
title: Text('分类'),
),
body: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: List.generate(
items,
(index) => ItemWidget(text: 'Item $index'),
),
),
),
);
},
),
);
}
}
class ItemWidget extends StatelessWidget {
const ItemWidget({super.key, required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Card(child: SizedBox(height: 100, child: Center(child: Text(text))));
}
}

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:item_tracker/screens/item_list_screen.dart';
import 'package:item_tracker/screens/statistics_screen.dart';
import 'package:item_tracker/screens/category_screens/category_screen.dart';
import 'package:item_tracker/screens/item_screens/item_list_screen.dart';
import 'package:item_tracker/screens/my_screen.dart'; //
class HomeScreen extends StatefulWidget {
@ -12,7 +12,7 @@ class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
static List<Widget> _widgetOptions = <Widget>[
ItemListScreen(),
StatisticsScreen(),
CategoryScreen(),
MyScreen(),
];
@ -37,6 +37,10 @@ class _HomeScreenState extends State<HomeScreen> {
icon: Icon(Icons.add_business_sharp),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.add_business_sharp),
label: '分类',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '我的',

View File

@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:item_tracker/provider/item_provider.dart';
import 'package:item_tracker/screens/item_screens/add_item_screen.dart';
import 'package:item_tracker/screens/item_screens/item_screen.dart';
import 'package:provider/provider.dart';
import 'item_screens/detail_item_screen.dart';
class ItemListScreen extends StatefulWidget {
@override
_ItemListScreenState createState() => _ItemListScreenState();
@ -52,7 +50,7 @@ class _ItemListScreenState extends State<ItemListScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddItemScreen(itemToEdit: item),
builder: (context) => ItemScreen(itemToEdit: item, ),
),
);
},
@ -63,7 +61,7 @@ class _ItemListScreenState extends State<ItemListScreen> {
onPressed: () async {
final shouldRefresh = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddItemScreen()),
MaterialPageRoute(builder: (context) => ItemScreen()),
);
print('shouldRefresh${shouldRefresh}');

View File

@ -40,18 +40,16 @@ extension ItemIsUseX on ItemIsUse {
}
}
class AddItemScreen extends StatefulWidget {
class ItemScreen extends StatefulWidget {
final Item? itemToEdit;
const AddItemScreen({Key? key, this.itemToEdit}) : super(key: key);
const ItemScreen({Key? key, this.itemToEdit}) : super(key: key);
@override
_FromTestRouteSate createState() => _FromTestRouteSate();
}
class _FromTestRouteSate extends State<AddItemScreen> {
class _FromTestRouteSate extends State<ItemScreen> {
String _name = '';
String _description = '';
String _location = '';
@ -126,7 +124,8 @@ class _FromTestRouteSate extends State<AddItemScreen> {
},
),
SizedBox(height: 28.0),
SubmitButton(onPressed: () {
SubmitButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
final newItem = Item(
name: _name,
@ -151,7 +150,8 @@ class _FromTestRouteSate extends State<AddItemScreen> {
// Navigator.pop
Navigator.pop(context, true);
}
})
},
isEdit: widget.itemToEdit != null)
],
),
);

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import '../add_item_screen.dart';
import '../item_screen.dart';
class ItemIsUseSelector extends StatelessWidget {
final ItemIsUse? selected;

View File

@ -2,15 +2,18 @@ import 'package:flutter/material.dart';
class SubmitButton extends StatelessWidget {
final VoidCallback onPressed;
const SubmitButton({required this.onPressed});
final bool isEdit;
const SubmitButton({required this.onPressed, this.isEdit = false});
@override
Widget build(BuildContext context) {
final buttonText = isEdit ? '保存' : '提交';
return ElevatedButton(
onPressed: onPressed,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text('提交'),
child: Text('${buttonText}'),
),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),

View File

@ -1,15 +0,0 @@
import 'package:flutter/material.dart';
class StatisticsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('统计信息'),
),
body: Center(
child: Text('这里将显示统计信息'),
),
);
}
}