feat:增加图表组件
This commit is contained in:
132
lib/layout/index.dart
Normal file
132
lib/layout/index.dart
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NavBar extends StatelessWidget {
|
||||||
|
final int currentIndex;
|
||||||
|
final Function(int) onTap;
|
||||||
|
|
||||||
|
static const List<BottomNavigationBarItem> navItems = [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.home_outlined),
|
||||||
|
activeIcon: Icon(Icons.home),
|
||||||
|
label: "记录",
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.pie_chart_outline),
|
||||||
|
activeIcon: Icon(Icons.pie_chart),
|
||||||
|
label: "统计",
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.group_outlined),
|
||||||
|
activeIcon: Icon(Icons.group),
|
||||||
|
label: "朋友圈",
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.account_circle_outlined),
|
||||||
|
activeIcon: Icon(Icons.account_circle),
|
||||||
|
label: "我的",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
const NavBar({super.key, required this.currentIndex, required this.onTap});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BottomNavigationBar(
|
||||||
|
currentIndex: currentIndex,
|
||||||
|
iconSize: 25,
|
||||||
|
type: BottomNavigationBarType.fixed,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
items: navItems,
|
||||||
|
onTap: onTap,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsDrawer extends StatelessWidget {
|
||||||
|
const SettingsDrawer({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// final themeProvider = Provider.of<ThemeProvider>(context);
|
||||||
|
|
||||||
|
return Drawer(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxHeight: 100),
|
||||||
|
child: DrawerHeader(
|
||||||
|
decoration: BoxDecoration(color: Theme.of(context).primaryColor),
|
||||||
|
child: const Text(
|
||||||
|
'设置',
|
||||||
|
style: TextStyle(color: Colors.white, fontSize: 24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.brightness_4),
|
||||||
|
title: const Text('夜间模式'),
|
||||||
|
// trailing: Switch(
|
||||||
|
// value: themeProvider.isDarkMode,
|
||||||
|
// onChanged: (bool value) {
|
||||||
|
// themeProvider.toggleTheme();
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// onTap: () => themeProvider.toggleTheme(),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 其他设置选项
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.notifications),
|
||||||
|
title: const Text('通知设置'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context); // 关闭抽屉
|
||||||
|
// 跳转到通知设置页面
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.language),
|
||||||
|
title: const Text('语言'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context); // 关闭抽屉
|
||||||
|
// 跳转到语言设置页面
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
// 底部关于
|
||||||
|
const Divider(),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.info),
|
||||||
|
title: const Text('关于我们'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context); // 关闭抽屉
|
||||||
|
// 跳转到关于页面
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<StatelessWidget> homeActions() {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.search, color: Colors.white),
|
||||||
|
onPressed: () {
|
||||||
|
// 搜索功能
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(Icons.settings, color: Colors.white),
|
||||||
|
onPressed: () {
|
||||||
|
Scaffold.of(context).openEndDrawer();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:food_hub_app/profile.dart';
|
import 'package:food_hub_app/views/home.dart';
|
||||||
import 'package:food_hub_app/record.dart';
|
|
||||||
import 'package:food_hub_app/views/login.dart';
|
import 'package:food_hub_app/views/login.dart';
|
||||||
import 'package:food_hub_app/views/recordForm.dart';
|
import 'package:food_hub_app/views/recordForm.dart';
|
||||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
@@ -19,7 +18,7 @@ class MyApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(fontFamily: 'CustomFont', primaryColor: Colors.green),
|
theme: ThemeData(fontFamily: 'CustomFont'),
|
||||||
supportedLocales: const [
|
supportedLocales: const [
|
||||||
Locale('en', 'US'), // 英语
|
Locale('en', 'US'), // 英语
|
||||||
Locale('zh', 'CN'), // 中文
|
Locale('zh', 'CN'), // 中文
|
||||||
@@ -41,89 +40,9 @@ class MyApp extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
home: LoginPage(),
|
home: LoginPage(),
|
||||||
routes: {
|
routes: {
|
||||||
'/home': (context) => MainPage(),
|
'/home': (context) => HomePage(),
|
||||||
'/recordForm': (context) => RecordFormPage(),
|
'/recordForm': (context) => RecordFormPage(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainPage extends StatefulWidget {
|
|
||||||
const MainPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _MainPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MainPage extends State<MainPage> {
|
|
||||||
int _currentIndex = 0;
|
|
||||||
|
|
||||||
final List<Widget> _tabPages = const [RecordPage(), ProfilePage()];
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
centerTitle: true,
|
|
||||||
title: Text('Food Hub'),
|
|
||||||
backgroundColor: Colors.green,
|
|
||||||
leading: IconButton(
|
|
||||||
icon: Icon(Icons.menu),
|
|
||||||
onPressed: () {
|
|
||||||
// 打开侧边栏或菜单
|
|
||||||
},
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.search),
|
|
||||||
onPressed: () {
|
|
||||||
// 搜索功能
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.more_vert),
|
|
||||||
onPressed: () {
|
|
||||||
// 更多选项
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
backgroundColor: Color(0xFFF5F5F5),
|
|
||||||
body: _tabPages[_currentIndex],
|
|
||||||
floatingActionButton:
|
|
||||||
_currentIndex == _tabPages.length - 1 ? null : addItemButton(context),
|
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
|
||||||
currentIndex: _currentIndex,
|
|
||||||
iconSize: 25,
|
|
||||||
type: BottomNavigationBarType.fixed,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
items: const [
|
|
||||||
BottomNavigationBarItem(icon: Icon(Icons.home), label: "记录"),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.account_circle),
|
|
||||||
label: "我的",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
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: Colors.green,
|
|
||||||
shape: const CircleBorder(),
|
|
||||||
child: Icon(Icons.add, color: Colors.white),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,14 +8,23 @@ class Recipe {
|
|||||||
final int commentCount;
|
final int commentCount;
|
||||||
|
|
||||||
Recipe(
|
Recipe(
|
||||||
this.name,
|
this.name,
|
||||||
this.category,
|
this.category,
|
||||||
this.date,
|
this.date,
|
||||||
this.imageUrl,
|
this.imageUrl,
|
||||||
this.likeCount,
|
this.likeCount,
|
||||||
this.favoriteCount,
|
this.favoriteCount,
|
||||||
this.commentCount,
|
this.commentCount,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Record {
|
||||||
|
final String name;
|
||||||
|
final String category;
|
||||||
|
final String date;
|
||||||
|
final String imageUrl;
|
||||||
|
|
||||||
|
Record(this.name, this.category, this.date, this.imageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ViewType { recipe, calendar, timeline }
|
enum ViewType { recipe, calendar, timeline }
|
||||||
|
|||||||
17
lib/utils/index.dart
Normal file
17
lib/utils/index.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
/// 解析日期选择器
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 格式化时间
|
||||||
|
String formatDateTime(DateTime dateTime, [String format = 'yyyy-MM-dd']) {
|
||||||
|
final DateFormat formatter = DateFormat(format);
|
||||||
|
// return formatter.format(dateTime);
|
||||||
|
return Intl.withLocale('zh_CN', () => formatter.format(dateTime));
|
||||||
|
}
|
||||||
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) {
|
void loginClick(BuildContext context) {
|
||||||
|
Navigator.pushNamed(context, '/home');
|
||||||
|
return;
|
||||||
|
|
||||||
// 表单校验通过才会继续执行
|
// 表单校验通过才会继续执行
|
||||||
if ((_formKey.currentState as FormState).validate()) {
|
if ((_formKey.currentState as FormState).validate()) {
|
||||||
(_formKey.currentState as FormState).save();
|
(_formKey.currentState as FormState).save();
|
||||||
|
TDMessage.showMessage(
|
||||||
|
context: context,
|
||||||
|
visible: true,
|
||||||
|
icon: true,
|
||||||
|
content: "登录成功",
|
||||||
|
theme: MessageTheme.success,
|
||||||
|
duration: 3000,
|
||||||
|
);
|
||||||
Navigator.pushNamed(context, '/home');
|
Navigator.pushNamed(context, '/home');
|
||||||
} else {
|
} 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("朋友圈"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import 'package:food_hub_app/widgets/recipe/recipe_list.dart';
|
|||||||
import 'package:food_hub_app/widgets/recipe/recipe_timeline.dart';
|
import 'package:food_hub_app/widgets/recipe/recipe_timeline.dart';
|
||||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||||
|
|
||||||
import 'models/recipe.dart';
|
import '../models/recipe.dart';
|
||||||
|
|
||||||
class RecordPage extends StatefulWidget {
|
class RecordPage extends StatefulWidget {
|
||||||
const RecordPage({super.key});
|
const RecordPage({super.key});
|
||||||
@@ -20,6 +20,11 @@ class _RecordPageState extends State<RecordPage>
|
|||||||
Recipe("红烧肉", "家常菜", "2025-05-05", "https://picsum.photos/200", 12, 4, 7),
|
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(
|
late final TabController _tabController = TabController(
|
||||||
length: 3,
|
length: 3,
|
||||||
vsync: this,
|
vsync: this,
|
||||||
@@ -39,7 +44,7 @@ class _RecordPageState extends State<RecordPage>
|
|||||||
// 定义标签列表
|
// 定义标签列表
|
||||||
final List<TDTab> tabs = [
|
final List<TDTab> tabs = [
|
||||||
const TDTab(text: '菜谱', icon: Icon(Icons.book)),
|
const TDTab(text: '菜谱', icon: Icon(Icons.book)),
|
||||||
const TDTab(text: '日历', icon: Icon(Icons.calendar_today)),
|
const TDTab(text: '日历', icon: Icon(Icons.calendar_month)),
|
||||||
const TDTab(text: '时间轴', icon: Icon(Icons.timeline)),
|
const TDTab(text: '时间轴', icon: Icon(Icons.timeline)),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -49,7 +54,6 @@ class _RecordPageState extends State<RecordPage>
|
|||||||
padding: EdgeInsets.all(0),
|
padding: EdgeInsets.all(0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// 标签栏
|
|
||||||
TDTabBar(
|
TDTabBar(
|
||||||
tabs: tabs,
|
tabs: tabs,
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.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:food_hub_app/widgets/common/index.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||||
@@ -28,29 +29,7 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
XFile? _selectedImage;
|
XFile? _selectedImage;
|
||||||
|
|
||||||
/// 整个表单存放的数据
|
/// 整个表单存放的数据
|
||||||
Map<String, dynamic> _formData = {
|
Map<String, dynamic> _formData = {"name": '', "date": '', "photo": ''};
|
||||||
"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": "",
|
|
||||||
};
|
|
||||||
|
|
||||||
/// 定义整个校验规则
|
/// 定义整个校验规则
|
||||||
final Map<String, TDFormValidation> _validationRules = {
|
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> files = [];
|
||||||
|
|
||||||
List<TDUploadFile> _onValueChanged(
|
List<TDUploadFile> _onValueChanged(
|
||||||
@@ -108,28 +79,28 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
/// 三个文本型的表格单元
|
|
||||||
for (var i = 0; i < 4; i++) {
|
|
||||||
_controller.add(TextEditingController());
|
|
||||||
}
|
|
||||||
_formData.forEach((key, value) {
|
|
||||||
_formItemNotifier[key] = FormItemNotifier();
|
|
||||||
});
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
// TODO: implement dispose
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
String parseDatePickerSelected(Map<String, int> selected) {
|
void _confirmClick(BuildContext context) {
|
||||||
final String year = selected['year'].toString().padLeft(4, '0');
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
final String month = selected['month'].toString().padLeft(2, '0');
|
// showSuccessToast("新增记录成功");
|
||||||
final String day = selected['day'].toString().padLeft(2, '0');
|
print(_formData);
|
||||||
|
} else {
|
||||||
return '$year-$month-$day';
|
TDMessage.showMessage(
|
||||||
|
context: context,
|
||||||
|
visible: true,
|
||||||
|
icon: true,
|
||||||
|
content: "请先输入信息",
|
||||||
|
theme: MessageTheme.error,
|
||||||
|
duration: 3000,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TDFormItem buildNameItem() {
|
TDFormItem buildNameItem() {
|
||||||
@@ -150,8 +121,8 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
additionInfoColor: TDTheme.of(context).errorColor6,
|
additionInfoColor: TDTheme.of(context).errorColor6,
|
||||||
showBottomDivider: false,
|
showBottomDivider: false,
|
||||||
onChanged: (val) {
|
onChanged: (value) {
|
||||||
_formData['name'] = val;
|
_formData['name'] = value;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -168,21 +139,19 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
hintText: '请选择完成时间',
|
hintText: '请选择完成时间',
|
||||||
select: _formData['date'],
|
select: _formData['date'],
|
||||||
selectFn: (BuildContext context) {
|
selectFn: (BuildContext context) {
|
||||||
|
DateTime now = DateTime.now();
|
||||||
TDPicker.showDatePicker(
|
TDPicker.showDatePicker(
|
||||||
context,
|
context,
|
||||||
title: '选择时间',
|
title: '选择时间',
|
||||||
onConfirm: (selected) {
|
onConfirm: (selected) {
|
||||||
setState(() {
|
setState(() {
|
||||||
print(selected);
|
_formData['date'] = parseDatePickerSelected(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);
|
|
||||||
});
|
});
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
dateStart: [1999, 01, 01],
|
dateStart: [2000, 01, 01],
|
||||||
dateEnd: [2050, 12, 31],
|
dateEnd: [2100, 12, 31],
|
||||||
initialDate: [2012, 1, 1],
|
initialDate: [now.year, now.month, now.day],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -194,7 +163,6 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
name: 'photo',
|
name: 'photo',
|
||||||
labelWidth: 82.0,
|
labelWidth: 82.0,
|
||||||
type: TDFormItemType.upLoadImg,
|
type: TDFormItemType.upLoadImg,
|
||||||
formItemNotifier: _formItemNotifier['photo'],
|
|
||||||
child: TDUpload(
|
child: TDUpload(
|
||||||
files: files,
|
files: files,
|
||||||
onError: print,
|
onError: print,
|
||||||
@@ -202,9 +170,7 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
onChange: ((imgList, type) {
|
onChange: ((imgList, type) {
|
||||||
files = _onValueChanged(files ?? [], imgList, type);
|
files = _onValueChanged(files ?? [], imgList, type);
|
||||||
List imgs = files.map((e) => e.remotePath ?? e.assetPath).toList();
|
List imgs = files.map((e) => e.remotePath ?? e.assetPath).toList();
|
||||||
setState(() {
|
setState(() {});
|
||||||
_formItemNotifier['photo'].upDataForm(imgs.join(','));
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -214,21 +180,16 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(color: TDTheme.of(context).whiteColor1),
|
decoration: BoxDecoration(color: TDTheme.of(context).whiteColor1),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TDButton(
|
child: TDButton(
|
||||||
text: '重置',
|
text: '取消',
|
||||||
type: TDButtonType.fill,
|
type: TDButtonType.fill,
|
||||||
theme: TDButtonTheme.light,
|
theme: TDButtonTheme.light,
|
||||||
shape: TDButtonShape.rectangle,
|
shape: TDButtonShape.rectangle,
|
||||||
onTap: () {
|
onTap: () => Navigator.pop(context),
|
||||||
//用户名称
|
|
||||||
_controller[0].clear();
|
|
||||||
//密码
|
|
||||||
_controller[1].clear();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 20),
|
SizedBox(width: 20),
|
||||||
@@ -238,7 +199,7 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
type: TDButtonType.fill,
|
type: TDButtonType.fill,
|
||||||
theme: TDButtonTheme.primary,
|
theme: TDButtonTheme.primary,
|
||||||
shape: TDButtonShape.rectangle,
|
shape: TDButtonShape.rectangle,
|
||||||
onTap: () => {},
|
onTap: () => _confirmClick(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -250,7 +211,14 @@ class _RecordFormPage extends State<RecordFormPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
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),
|
backgroundColor: Color(0xFFF5F5F5),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.all(10),
|
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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:food_hub_app/utils/index.dart';
|
||||||
import 'package:table_calendar/table_calendar.dart';
|
import 'package:table_calendar/table_calendar.dart';
|
||||||
|
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||||
|
|
||||||
class RecipeCalendar extends StatefulWidget {
|
class RecipeCalendar extends StatefulWidget {
|
||||||
const RecipeCalendar({super.key});
|
const RecipeCalendar({super.key});
|
||||||
@@ -9,49 +11,103 @@ class RecipeCalendar extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _RecipeCalendarState extends State<RecipeCalendar> {
|
class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||||
// 当前选中的日期
|
|
||||||
DateTime _selectedDay = DateTime.now();
|
DateTime _selectedDay = DateTime.now();
|
||||||
// 当前显示的月份
|
|
||||||
DateTime _focusedDay = DateTime.now();
|
DateTime _focusedDay = DateTime.now();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TableCalendar recipeCalendar() {
|
||||||
|
return TableCalendar(
|
||||||
|
locale: 'zh_CN',
|
||||||
|
headerStyle: const HeaderStyle(
|
||||||
|
formatButtonVisible: false,
|
||||||
|
titleCentered: true,
|
||||||
|
),
|
||||||
|
firstDay: DateTime.utc(2010, 1, 1),
|
||||||
|
lastDay: DateTime.utc(2100, 12, 31),
|
||||||
|
focusedDay: _focusedDay,
|
||||||
|
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
|
||||||
|
onDaySelected: (selectedDay, focusedDay) {
|
||||||
|
if (!isSameDay(_selectedDay, selectedDay)) {
|
||||||
|
setState(() {
|
||||||
|
_selectedDay = selectedDay;
|
||||||
|
_focusedDay = focusedDay;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onPageChanged: (focusedDay) {
|
||||||
|
print("cxx");
|
||||||
|
_focusedDay = focusedDay;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget dailyItem() {
|
||||||
|
return Card(
|
||||||
|
elevation: 0,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(formatDateTime(_selectedDay, 'MM月dd日 EEEE')),
|
||||||
|
recipeRecordItem(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget recipeRecordItem() {
|
||||||
|
return Card(
|
||||||
|
elevation: 0,
|
||||||
|
color: Colors.limeAccent.shade100,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
TDAvatar(
|
||||||
|
size: TDAvatarSize.medium,
|
||||||
|
type: TDAvatarType.customText,
|
||||||
|
text: 'A',
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
// 使用Expanded让文本区域占据剩余空间
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"青椒土豆丝",
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Text("家常菜", style: TextStyle(color: Colors.grey)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
TDButton(
|
||||||
|
icon: TDIcons.arrow_right,
|
||||||
|
type: TDButtonType.fill,
|
||||||
|
shape: TDButtonShape.circle,
|
||||||
|
theme: TDButtonTheme.primary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Card(
|
Card(elevation: 0, color: Colors.white, child: recipeCalendar()),
|
||||||
elevation: 0,
|
const SizedBox(height: 10),
|
||||||
color: Colors.white,
|
dailyItem(),
|
||||||
child: TableCalendar(
|
|
||||||
locale: 'zh_CN',
|
|
||||||
headerStyle: const HeaderStyle(
|
|
||||||
formatButtonVisible: false,
|
|
||||||
titleCentered: true,
|
|
||||||
),
|
|
||||||
firstDay: DateTime.utc(2010, 1, 1),
|
|
||||||
lastDay: DateTime.utc(2100, 12, 31),
|
|
||||||
focusedDay: _focusedDay,
|
|
||||||
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
|
|
||||||
onDaySelected: (selectedDay, focusedDay) {
|
|
||||||
if (!isSameDay(_selectedDay, selectedDay)) {
|
|
||||||
setState(() {
|
|
||||||
_selectedDay = selectedDay;
|
|
||||||
_focusedDay = focusedDay;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPageChanged: (focusedDay) {
|
|
||||||
print("cxx");
|
|
||||||
_focusedDay = focusedDay;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// 显示选中的日期
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
'选中的日期: ${_selectedDay.year}-${_selectedDay.month}-${_selectedDay.day}',
|
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
32
pubspec.lock
32
pubspec.lock
@@ -65,6 +65,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.0"
|
version: "3.4.0"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -105,27 +113,19 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.3+4"
|
version: "0.9.3+4"
|
||||||
|
fl_chart:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fl_chart
|
||||||
|
sha256: "577aeac8ca414c25333334d7c4bb246775234c0e44b38b10a82b559dd4d764e7"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_date_pickers:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_date_pickers
|
|
||||||
sha256: "302b1200d8859ec0bfe51c4eaea5f4911d1dbfc3c3f7d256dcf32d99fec219ee"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.3"
|
|
||||||
flutter_datetime_picker_plus:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_datetime_picker_plus
|
|
||||||
sha256: "7d82da02c4e070bb28a9107de119ad195e2319b45c786fecc13482a9ffcc51da"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
flutter_form_builder:
|
flutter_form_builder:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
name: food_hub_app
|
name: food_hub_app
|
||||||
Reference in New Issue
Block a user