feat:增加图表组件
This commit is contained in:
73
lib/views/home.dart
Normal file
73
lib/views/home.dart
Normal file
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/layout/index.dart';
|
||||
import 'package:food_hub_app/views/moment.dart';
|
||||
import 'package:food_hub_app/views/profile.dart';
|
||||
import 'package:food_hub_app/views/record.dart';
|
||||
import 'package:food_hub_app/views/stats.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _HomePage();
|
||||
}
|
||||
|
||||
class _HomePage extends State<HomePage> {
|
||||
int _currentIndex = 0;
|
||||
|
||||
final List<Widget> _tabPages = const [
|
||||
RecordPage(),
|
||||
StatsPage(),
|
||||
MomentPage(),
|
||||
ProfilePage(),
|
||||
];
|
||||
|
||||
bool isShow(int index) {
|
||||
return index == 0 || index == 2;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text('Food Hub', style: TextStyle(color: Colors.white)),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.menu, color: Colors.white),
|
||||
onPressed: () {
|
||||
// 打开侧边栏或菜单
|
||||
},
|
||||
),
|
||||
actions: homeActions(),
|
||||
),
|
||||
backgroundColor: Color(0xFFF5F5F5),
|
||||
endDrawer: const SettingsDrawer(),
|
||||
body: _tabPages[_currentIndex],
|
||||
floatingActionButton:
|
||||
isShow(_currentIndex) ? addItemButton(context) : null,
|
||||
bottomNavigationBar: NavBar(
|
||||
currentIndex: _currentIndex,
|
||||
onTap: (index) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget addItemButton(BuildContext context) {
|
||||
void onAddItemClick(BuildContext context) {
|
||||
Navigator.pushNamed(context, '/recordForm');
|
||||
}
|
||||
|
||||
return FloatingActionButton(
|
||||
mini: true,
|
||||
onPressed: () => onAddItemClick(context),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
shape: const CircleBorder(),
|
||||
child: Icon(Icons.add, color: Colors.white),
|
||||
);
|
||||
}
|
||||
@@ -24,12 +24,30 @@ class _LoginPage extends State<LoginPage> {
|
||||
];
|
||||
|
||||
void loginClick(BuildContext context) {
|
||||
Navigator.pushNamed(context, '/home');
|
||||
return;
|
||||
|
||||
// 表单校验通过才会继续执行
|
||||
if ((_formKey.currentState as FormState).validate()) {
|
||||
(_formKey.currentState as FormState).save();
|
||||
TDMessage.showMessage(
|
||||
context: context,
|
||||
visible: true,
|
||||
icon: true,
|
||||
content: "登录成功",
|
||||
theme: MessageTheme.success,
|
||||
duration: 3000,
|
||||
);
|
||||
Navigator.pushNamed(context, '/home');
|
||||
} else {
|
||||
// showErrorToast("请先输入信息");
|
||||
TDMessage.showMessage(
|
||||
context: context,
|
||||
visible: true,
|
||||
icon: true,
|
||||
content: "请先输入信息",
|
||||
theme: MessageTheme.error,
|
||||
duration: 3000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
lib/views/moment.dart
Normal file
15
lib/views/moment.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MomentPage extends StatefulWidget {
|
||||
const MomentPage({super.key});
|
||||
|
||||
@override
|
||||
State<MomentPage> createState() => _MomentPage();
|
||||
}
|
||||
|
||||
class _MomentPage extends State<MomentPage>{
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(child: Text("朋友圈"));
|
||||
}
|
||||
}
|
||||
15
lib/views/profile.dart
Normal file
15
lib/views/profile.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ProfilePage extends StatefulWidget {
|
||||
const ProfilePage({super.key});
|
||||
|
||||
@override
|
||||
State<ProfilePage> createState() => _ProfilePage();
|
||||
}
|
||||
|
||||
class _ProfilePage extends State<ProfilePage>{
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(child: Text("个人主页"));
|
||||
}
|
||||
}
|
||||
80
lib/views/record.dart
Normal file
80
lib/views/record.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/widgets/recipe/recipe_calendar.dart';
|
||||
import 'package:food_hub_app/widgets/recipe/recipe_list.dart';
|
||||
import 'package:food_hub_app/widgets/recipe/recipe_timeline.dart';
|
||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||
|
||||
import '../models/recipe.dart';
|
||||
|
||||
class RecordPage extends StatefulWidget {
|
||||
const RecordPage({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _RecordPageState();
|
||||
}
|
||||
|
||||
class _RecordPageState extends State<RecordPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final List<Recipe> recipeList = [
|
||||
Recipe("韭菜炒鸡蛋", "家常菜", "2025-05-04", "https://picsum.photos/200", 10, 2, 4),
|
||||
Recipe("红烧肉", "家常菜", "2025-05-05", "https://picsum.photos/200", 12, 4, 7),
|
||||
];
|
||||
|
||||
final List<Record> recordList = [
|
||||
Record("韭菜炒鸡蛋", "家常菜", "2025-05-04", "https://picsum.photos/200"),
|
||||
Record("红烧肉", "家常菜", "2025-05-05", "https://picsum.photos/200"),
|
||||
];
|
||||
|
||||
late final TabController _tabController = TabController(
|
||||
length: 3,
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 定义标签列表
|
||||
final List<TDTab> tabs = [
|
||||
const TDTab(text: '菜谱', icon: Icon(Icons.book)),
|
||||
const TDTab(text: '日历', icon: Icon(Icons.calendar_month)),
|
||||
const TDTab(text: '时间轴', icon: Icon(Icons.timeline)),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.all(0),
|
||||
child: Column(
|
||||
children: [
|
||||
TDTabBar(
|
||||
tabs: tabs,
|
||||
controller: _tabController,
|
||||
backgroundColor: Colors.white,
|
||||
showIndicator: true,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
RecipeList(recipeList: recipeList),
|
||||
RecipeCalendar(),
|
||||
RecipeTimeline(recipeList: recipeList),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:food_hub_app/utils/index.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||
@@ -28,29 +29,7 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
XFile? _selectedImage;
|
||||
|
||||
/// 整个表单存放的数据
|
||||
Map<String, dynamic> _formData = {
|
||||
"name": '',
|
||||
"date": '',
|
||||
"gender": '',
|
||||
"birth": '',
|
||||
"place": '',
|
||||
"age": "2",
|
||||
"description": "2",
|
||||
"resume": '',
|
||||
"photo": '',
|
||||
};
|
||||
|
||||
Map<String, dynamic> _formItemNotifier = {
|
||||
"name": '',
|
||||
"password": '',
|
||||
"gender": '',
|
||||
"birth": '',
|
||||
"place": '',
|
||||
"age": '2',
|
||||
"description": '',
|
||||
"resume": '',
|
||||
"photo": "",
|
||||
};
|
||||
Map<String, dynamic> _formData = {"name": '', "date": '', "photo": ''};
|
||||
|
||||
/// 定义整个校验规则
|
||||
final Map<String, TDFormValidation> _validationRules = {
|
||||
@@ -71,14 +50,6 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
),
|
||||
};
|
||||
|
||||
void confirmClick(BuildContext context) {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
// showSuccessToast("新增记录成功");
|
||||
} else {
|
||||
// showErrorToast("请先提交信息");
|
||||
}
|
||||
}
|
||||
|
||||
List<TDUploadFile> files = [];
|
||||
|
||||
List<TDUploadFile> _onValueChanged(
|
||||
@@ -108,28 +79,28 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
/// 三个文本型的表格单元
|
||||
for (var i = 0; i < 4; i++) {
|
||||
_controller.add(TextEditingController());
|
||||
}
|
||||
_formData.forEach((key, value) {
|
||||
_formItemNotifier[key] = FormItemNotifier();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// TODO: implement dispose
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
String parseDatePickerSelected(Map<String, int> selected) {
|
||||
final String year = selected['year'].toString().padLeft(4, '0');
|
||||
final String month = selected['month'].toString().padLeft(2, '0');
|
||||
final String day = selected['day'].toString().padLeft(2, '0');
|
||||
|
||||
return '$year-$month-$day';
|
||||
void _confirmClick(BuildContext context) {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
// showSuccessToast("新增记录成功");
|
||||
print(_formData);
|
||||
} else {
|
||||
TDMessage.showMessage(
|
||||
context: context,
|
||||
visible: true,
|
||||
icon: true,
|
||||
content: "请先输入信息",
|
||||
theme: MessageTheme.error,
|
||||
duration: 3000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TDFormItem buildNameItem() {
|
||||
@@ -150,8 +121,8 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
backgroundColor: Colors.white,
|
||||
additionInfoColor: TDTheme.of(context).errorColor6,
|
||||
showBottomDivider: false,
|
||||
onChanged: (val) {
|
||||
_formData['name'] = val;
|
||||
onChanged: (value) {
|
||||
_formData['name'] = value;
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -168,21 +139,19 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
hintText: '请选择完成时间',
|
||||
select: _formData['date'],
|
||||
selectFn: (BuildContext context) {
|
||||
DateTime now = DateTime.now();
|
||||
TDPicker.showDatePicker(
|
||||
context,
|
||||
title: '选择时间',
|
||||
onConfirm: (selected) {
|
||||
setState(() {
|
||||
print(selected);
|
||||
_selected_1 =
|
||||
'${selected['year'].toString().padLeft(4, '0')}-${selected['month'].toString().padLeft(2, '0')}-${selected['day'].toString().padLeft(2, '0')}';
|
||||
_formItemNotifier['birth']?.upDataForm(_selected_1);
|
||||
_formData['date'] = parseDatePickerSelected(selected);
|
||||
});
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
dateStart: [1999, 01, 01],
|
||||
dateEnd: [2050, 12, 31],
|
||||
initialDate: [2012, 1, 1],
|
||||
dateStart: [2000, 01, 01],
|
||||
dateEnd: [2100, 12, 31],
|
||||
initialDate: [now.year, now.month, now.day],
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -194,7 +163,6 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
name: 'photo',
|
||||
labelWidth: 82.0,
|
||||
type: TDFormItemType.upLoadImg,
|
||||
formItemNotifier: _formItemNotifier['photo'],
|
||||
child: TDUpload(
|
||||
files: files,
|
||||
onError: print,
|
||||
@@ -202,9 +170,7 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
onChange: ((imgList, type) {
|
||||
files = _onValueChanged(files ?? [], imgList, type);
|
||||
List imgs = files.map((e) => e.remotePath ?? e.assetPath).toList();
|
||||
setState(() {
|
||||
_formItemNotifier['photo'].upDataForm(imgs.join(','));
|
||||
});
|
||||
setState(() {});
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -214,21 +180,16 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: TDTheme.of(context).whiteColor1),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TDButton(
|
||||
text: '重置',
|
||||
text: '取消',
|
||||
type: TDButtonType.fill,
|
||||
theme: TDButtonTheme.light,
|
||||
shape: TDButtonShape.rectangle,
|
||||
onTap: () {
|
||||
//用户名称
|
||||
_controller[0].clear();
|
||||
//密码
|
||||
_controller[1].clear();
|
||||
},
|
||||
onTap: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
@@ -238,7 +199,7 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
type: TDButtonType.fill,
|
||||
theme: TDButtonTheme.primary,
|
||||
shape: TDButtonShape.rectangle,
|
||||
onTap: () => {},
|
||||
onTap: () => _confirmClick(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -250,7 +211,14 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('新增记录'), backgroundColor: Colors.white),
|
||||
appBar: AppBar(
|
||||
title: Text('新增记录', style: TextStyle(color: Colors.white)),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context)
|
||||
),
|
||||
),
|
||||
backgroundColor: Color(0xFFF5F5F5),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
|
||||
35
lib/views/stats.dart
Normal file
35
lib/views/stats.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StatsPage extends StatefulWidget {
|
||||
const StatsPage({super.key});
|
||||
|
||||
@override
|
||||
State<StatsPage> createState() => _StatsPage();
|
||||
}
|
||||
|
||||
class _StatsPage extends State<StatsPage>{
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 月度做菜次数数据
|
||||
final List<FlSpot> spots = [
|
||||
FlSpot(0, 13), FlSpot(1, 32), FlSpot(2, 121),
|
||||
FlSpot(3, 31), FlSpot(4, 34), FlSpot(5, 45)
|
||||
];
|
||||
|
||||
// 月份标签
|
||||
final List<String> months = [
|
||||
'1月', '2月', '3月', '4月', '5月', '6月'
|
||||
];
|
||||
|
||||
return LineChart(
|
||||
LineChartData(
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
spots: spots,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user