From 0f106f6adc78f488726b5e3bc560e4128efa5897 Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Fri, 7 Nov 2025 14:33:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9B=B4=E6=96=B0=E5=BE=85=E5=8A=9E?= =?UTF-8?q?=E4=BA=8B=E9=A1=B9=E5=AF=B9=E8=AF=9D=E6=A1=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 2 +- lib/layout/main_screen.dart | 113 ++++----- lib/main.dart | 22 +- lib/models/todo.dart | 33 +-- lib/pages/flash_page.dart | 3 +- lib/pages/notes_page.dart | 3 +- lib/pages/reminders_page.dart | 3 +- lib/pages/todo_page.dart | 156 +++++------- lib/provider/app_provider.dart | 5 +- lib/provider/todo_provider.dart | 21 ++ lib/store/todo_dialog_store.dart | 82 ------- lib/utils/toast_util.dart | 48 ++++ lib/widgets/awesome_dialog.dart | 54 +++++ lib/widgets/common.dart | 27 +-- lib/widgets/todo_dialog.dart | 271 --------------------- lib/widgets/todo_form.dart | 292 +++++++++++++++++++++++ lib/widgets/todo_widget.dart | 37 ++- pubspec.lock | 59 +++-- pubspec.yaml | 95 +------- 19 files changed, 626 insertions(+), 700 deletions(-) create mode 100644 lib/provider/todo_provider.dart delete mode 100644 lib/store/todo_dialog_store.dart create mode 100644 lib/utils/toast_util.dart create mode 100644 lib/widgets/awesome_dialog.dart delete mode 100644 lib/widgets/todo_dialog.dart create mode 100644 lib/widgets/todo_form.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 10ec710..b5a3ee0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ { final GlobalKey _todoPageKey = GlobalKey(); + List navItems = [ + BottomNavigationBarItem(icon: Icon(Icons.flash_on), label: '闪灵'), + BottomNavigationBarItem(icon: Icon(Icons.note), label: '笔记'), + BottomNavigationBarItem(icon: Icon(Icons.checklist), label: '待办'), + BottomNavigationBarItem(icon: Icon(Icons.notifications), label: '提醒'), + ]; + @override Widget build(BuildContext context) { return Consumer( - builder: (context, appState, child) { + builder: (context, appProvider, child) { return Scaffold( drawer: const AppDrawer(), appBar: AppBar( - title: Text(_getAppBarTitle(appState.currentIndex)), + title: Text(_getAppBarTitle(appProvider.currentIndex)), backgroundColor: Colors.blue, foregroundColor: Colors.white, elevation: 0, ), - body: _buildPage(appState.currentIndex), + body: _buildPage(appProvider.currentIndex), bottomNavigationBar: BottomNavigationBar( - currentIndex: appState.currentIndex, - onTap: (index) => appState.changeTab(index), + currentIndex: appProvider.currentIndex, + onTap: (index) => appProvider.changeTab(index), type: BottomNavigationBarType.fixed, - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.flash_on), - label: '闪灵', - ), - BottomNavigationBarItem( - icon: Icon(Icons.note), - label: '笔记', - ), - BottomNavigationBarItem( - icon: Icon(Icons.checklist), - label: '待办', - ), - BottomNavigationBarItem( - icon: Icon(Icons.notifications), - label: '提醒', - ), - ], + backgroundColor: Colors.white, + items: navItems, ), - floatingActionButton: _buildFloatingActionButton(appState.currentIndex), + floatingActionButton: _buildFloatingButton(appProvider.currentIndex), ); }, ); } - Widget? _buildFloatingActionButton(int currentIndex) { - if (currentIndex == 2) { - return FloatingActionButton( - onPressed: _showAddTodoDialog, - backgroundColor: Colors.transparent, - elevation: 0, - child: Container( - width: 50, - height: 50, - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - colors: [Colors.orange.shade300, Colors.orange.shade500], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - boxShadow: [ - BoxShadow( - color: Colors.orange.shade100, - blurRadius: 12, - offset: Offset(0, 6), - ), - ], - ), - child: Icon(Icons.add, color: Colors.white, size: 36), - ), - ); + void _onPressFloatingButton(int index) { + if (index == 2) { + if (_todoPageKey.currentState != null) { + _todoPageKey.currentState!.showAddTodoDialog(); + } } - return null; } - void _showAddTodoDialog() { - // 通过 GlobalKey 调用 TodoPage 的方法 - if (_todoPageKey.currentState != null) { - _todoPageKey.currentState!.showAddTodoDialog(); - } + Widget _buildFloatingButton(int index) { + return FloatingActionButton( + onPressed: () => _onPressFloatingButton(index), + backgroundColor: Colors.transparent, + elevation: 0, + shape: CircleBorder(), + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [Colors.orange.shade300, Colors.orange.shade500], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + boxShadow: [ + BoxShadow( + color: Colors.orange.shade100, + blurRadius: 12, + offset: Offset(0, 6), + ), + ], + ), + child: Icon(Icons.add, color: Colors.white, size: 36), + ), + ); } Widget _buildPage(int index) { @@ -113,12 +103,7 @@ class _MainScreenState extends State { } String _getAppBarTitle(int index) { - final titles = { - 0: '闪灵', - 1: '笔记', - 2: '待办事项', - 3: '提醒任务', - }; + final titles = {0: '闪灵', 1: '笔记', 2: '待办', 3: '提醒'}; return titles[index] ?? '闪灵'; } -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index 6b4efcf..39eff1a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,10 @@ import 'package:flisp_app/provider/app_provider.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; import 'layout/main_screen.dart'; -import 'store/todo_dialog_store.dart'; +import 'provider/todo_provider.dart'; void main() { runApp(const MyApp()); @@ -17,16 +18,23 @@ class MyApp extends StatelessWidget { return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => AppProvider()), - ChangeNotifierProvider(create: (_) => TodoDialogStore()), // 新增 + ChangeNotifierProvider(create: (_) => TodoProvider()), // 新增 ], child: MaterialApp( title: '闪灵', - theme: ThemeData( - primarySwatch: Colors.blue, - useMaterial3: true, - ), + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [ + const Locale('zh'), + const Locale('zh', 'CN'), + ], + locale: Locale('zh', 'CN'), + theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true), home: const MainScreen(), ), ); } -} \ No newline at end of file +} diff --git a/lib/models/todo.dart b/lib/models/todo.dart index ad476ea..1debf25 100644 --- a/lib/models/todo.dart +++ b/lib/models/todo.dart @@ -1,45 +1,48 @@ import 'package:flutter/material.dart'; class TodoItem { - String id; + num id; String title; - String? description; + String content; bool isCompleted; - DateTime createdAt; DateTime? dueDate; TodoPriority priority; - String? category; TodoItem({ required this.id, required this.title, - this.description, + required this.content, this.isCompleted = false, - DateTime? createdAt, this.dueDate, this.priority = TodoPriority.medium, - this.category, - }) : createdAt = createdAt ?? DateTime.now(); + }); TodoItem copyWith({ - String? id, + num? id, String? title, - String? description, + String? content, bool? isCompleted, - DateTime? createdAt, DateTime? dueDate, TodoPriority? priority, - String? category, }) { return TodoItem( id: id ?? this.id, title: title ?? this.title, - description: description ?? this.description, + content: content ?? this.content, isCompleted: isCompleted ?? this.isCompleted, - createdAt: createdAt ?? this.createdAt, dueDate: dueDate ?? this.dueDate, priority: priority ?? this.priority, - category: category ?? this.category, + ); + } + + static TodoItem getEmpty() { + return TodoItem( + id: 0, + title: '', + content: '', + isCompleted: false, + dueDate: null, + priority: TodoPriority.medium, ); } } diff --git a/lib/pages/flash_page.dart b/lib/pages/flash_page.dart index 063bc57..6446222 100644 --- a/lib/pages/flash_page.dart +++ b/lib/pages/flash_page.dart @@ -1,3 +1,4 @@ +import 'package:flisp_app/widgets/common.dart'; import 'package:flutter/material.dart'; class FlashPage extends StatelessWidget { @@ -5,7 +6,7 @@ class FlashPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Center( + return buildBody( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/pages/notes_page.dart b/lib/pages/notes_page.dart index d14daac..0b6b148 100644 --- a/lib/pages/notes_page.dart +++ b/lib/pages/notes_page.dart @@ -1,3 +1,4 @@ +import 'package:flisp_app/widgets/common.dart'; import 'package:flutter/material.dart'; class NotesPage extends StatelessWidget { @@ -5,7 +6,7 @@ class NotesPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Center( + return buildBody( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/pages/reminders_page.dart b/lib/pages/reminders_page.dart index e67b565..cb88931 100644 --- a/lib/pages/reminders_page.dart +++ b/lib/pages/reminders_page.dart @@ -1,3 +1,4 @@ +import 'package:flisp_app/widgets/common.dart'; import 'package:flutter/material.dart'; class RemindersPage extends StatelessWidget { @@ -5,7 +6,7 @@ class RemindersPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Center( + return buildBody( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/pages/todo_page.dart b/lib/pages/todo_page.dart index 21e8526..45ca191 100644 --- a/lib/pages/todo_page.dart +++ b/lib/pages/todo_page.dart @@ -1,12 +1,13 @@ -import 'package:flisp_app/store/todo_dialog_store.dart'; +import 'package:flisp_app/provider/todo_provider.dart'; +import 'package:flisp_app/widgets/awesome_dialog.dart'; import 'package:flisp_app/widgets/todo_widget.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:provider/provider.dart'; -import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flisp_app/models/todo.dart'; import 'package:flisp_app/utils/todo_utils.dart'; import 'package:flisp_app/widgets/common.dart'; -import 'package:flisp_app/widgets/todo_dialog.dart'; +import 'package:flisp_app/widgets/todo_form.dart'; class TodoPage extends StatefulWidget { final VoidCallback? onAddTodoPressed; @@ -30,7 +31,7 @@ class TodoPageState extends State { // 添加一个公共方法供外部调用 void showAddTodoDialog() { - _showAddTodoDialog(); + _showTodoDialog(false, null); } @override @@ -60,99 +61,22 @@ class TodoPageState extends State { return _activeTodos.isEmpty ? buildEmptyState(_currentTab) : buildTodoList( - todos: _activeTodos, - onToggleTodo: (todoId) { - setState(() { - _toggleTodo(todoId); - }); - }, - onEditTodo: (todo) { - _showEditTodoDialog(todo); - } - ); - } - - // 显示添加待办事项对话框 - void _showAddTodoDialog() { - final store = Provider.of(context, listen: false); - store.reset(); - - showAwesomeDialog( - context: context, - body: TodoDialog(isEditing: false), - onOk: () { - if (store.validate()) { - _saveTodo(false, null); - } - }, - onCancel: () { - store.reset(); - }, - ); - } - - // 显示编辑待办事项对话框 - void _showEditTodoDialog(TodoItem todo) { - final store = Provider.of(context, listen: false); - store.initEditData(todo); - - showAwesomeDialog( - context: context, - body: TodoDialog(isEditing: true, initialTodo: todo), - onOk: () { - if (store.validate()) { - _saveTodo(true, todo); - } - }, - onCancel: () { - store.reset(); - }, - ); - } - - // 保存待办事项 - void _saveTodo(bool isEditing, TodoItem? todo) { - final store = Provider.of(context, listen: false); - - final newTodo = store.createTodoItem( - id: isEditing ? todo!.id : null, - isCompleted: isEditing ? todo!.isCompleted : false, - createdAt: isEditing ? todo!.createdAt : null, - ); - - setState(() { - if (isEditing) { - final index = _todos.indexWhere((t) => t.id == todo!.id); - if (index != -1) { - _todos[index] = newTodo; - } - } else { - _todos.insert(0, newTodo); - } - }); - - store.reset(); - _showSuccessDialog(isEditing ? '更新成功' : '添加成功'); - } - - // 显示成功提示 - void _showSuccessDialog(String message) { - AwesomeDialog( - context: context, - dialogType: DialogType.success, - animType: AnimType.scale, - title: message, - btnOkText: "好的", - btnOkColor: Colors.green, - btnOkOnPress: () {}, - autoHide: Duration(seconds: 2), - ).show(); + todos: _activeTodos, + onToggleTodo: (todoId) { + setState(() { + _toggleTodo(todoId); + }); + }, + onEditTodo: (todo) { + _showTodoDialog(true, todo); + }, + ); } // 切换待办事项完成状态 void _toggleTodo(String id) { setState(() { - final index = _todos.indexWhere((todo) => todo.id == id); + final index = _todos.indexWhere((todo) => todo.id.toString() == id); if (index != -1) { _todos[index] = _todos[index].copyWith( isCompleted: !_todos[index].isCompleted, @@ -160,4 +84,50 @@ class TodoPageState extends State { } }); } -} \ No newline at end of file + + // 显示对话框 + void _showTodoDialog(bool isEditing, TodoItem? todo) { + final todoProvider = Provider.of(context, listen: false); + + if (isEditing) { + todoProvider.initForm(todo!); + } else { + todoProvider.resetForm(); + } + + final formKey = GlobalKey(); + + showAwesomeDialog( + context: context, + body: TodoForm(formKey: formKey, isEditing: isEditing, initialTodo: todo), + onOk: () { + if (formKey.currentState!.saveAndValidate()) { + Navigator.of(context).pop(); + _saveTodo(isEditing, todoProvider.formItem); + } + }, + onCancel: () { + todoProvider.resetForm(); + }, + ); + } + + // 保存待办事项 + void _saveTodo(bool isEditing, TodoItem todo) { + final store = Provider.of(context, listen: false); + + setState(() { + if (isEditing) { + final index = _todos.indexWhere((t) => t.id == todo.id); + if (index != -1) { + _todos[index] = todo; + } + } else { + _todos.insert(0, todo); + } + }); + + store.resetForm(); + showSuccessDialog(context, isEditing ? '更新成功' : '添加成功'); + } +} diff --git a/lib/provider/app_provider.dart b/lib/provider/app_provider.dart index 5777adf..af2369a 100644 --- a/lib/provider/app_provider.dart +++ b/lib/provider/app_provider.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; -class AppProvider with ChangeNotifier { +class AppProvider with ChangeNotifier { int _currentIndex = 0; + int get currentIndex => _currentIndex; void changeTab(int index) { _currentIndex = index; notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/provider/todo_provider.dart b/lib/provider/todo_provider.dart new file mode 100644 index 0000000..7528959 --- /dev/null +++ b/lib/provider/todo_provider.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flisp_app/models/todo.dart'; + +class TodoProvider with ChangeNotifier { + TodoItem _formItem = TodoItem.getEmpty(); + + TodoItem get formItem => _formItem; + + void resetForm() { + _formItem = TodoItem.getEmpty(); + notifyListeners(); + } + + void initForm(TodoItem todo) { + _formItem.title = todo.title; + _formItem.content = todo.content ?? ''; + _formItem.dueDate = todo.dueDate; + _formItem.priority = todo.priority; + notifyListeners(); + } +} diff --git a/lib/store/todo_dialog_store.dart b/lib/store/todo_dialog_store.dart deleted file mode 100644 index 592dfb9..0000000 --- a/lib/store/todo_dialog_store.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flisp_app/models/todo.dart'; - -class TodoDialogStore extends ChangeNotifier { - String _title = ''; - String _description = ''; - DateTime? _dueDate; - TodoPriority _priority = TodoPriority.medium; - String? _category; - - // Getters - String get title => _title; - String get description => _description; - DateTime? get dueDate => _dueDate; - TodoPriority get priority => _priority; - String? get category => _category; - - // Setters - set title(String value) { - _title = value; - notifyListeners(); - } - - set description(String value) { - _description = value; - notifyListeners(); - } - - set dueDate(DateTime? value) { - _dueDate = value; - notifyListeners(); - } - - set priority(TodoPriority value) { - _priority = value; - notifyListeners(); - } - - set category(String? value) { - _category = value; - notifyListeners(); - } - - // 初始化编辑数据 - void initEditData(TodoItem todo) { - _title = todo.title; - _description = todo.description ?? ''; - _dueDate = todo.dueDate; - _priority = todo.priority; - _category = todo.category; - notifyListeners(); - } - - // 重置表单数据 - void reset() { - _title = ''; - _description = ''; - _dueDate = null; - _priority = TodoPriority.medium; - _category = null; - notifyListeners(); - } - - // 验证表单 - bool validate() { - return _title.trim().isNotEmpty; - } - - // 创建待办事项对象 - TodoItem createTodoItem({String? id, bool isCompleted = false, DateTime? createdAt}) { - return TodoItem( - id: id ?? DateTime.now().millisecondsSinceEpoch.toString(), - title: _title.trim(), - description: _description.trim().isEmpty ? null : _description.trim(), - dueDate: _dueDate, - priority: _priority, - category: _category, - isCompleted: isCompleted, - createdAt: createdAt ?? DateTime.now(), - ); - } -} \ No newline at end of file diff --git a/lib/utils/toast_util.dart b/lib/utils/toast_util.dart new file mode 100644 index 0000000..d471e03 --- /dev/null +++ b/lib/utils/toast_util.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class ToastUtil { + static void success(String message) { + Fluttertoast.showToast( + msg: message, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.TOP, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16.0, + ); + } + + static void error(String message) { + Fluttertoast.showToast( + msg: message, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.TOP, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16.0, + ); + } + + static void warning(String message) { + Fluttertoast.showToast( + msg: message, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.TOP, + backgroundColor: Colors.orange, + textColor: Colors.white, + fontSize: 16.0, + ); + } + + static void info(String message) { + Fluttertoast.showToast( + msg: message, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.TOP, + backgroundColor: Colors.blue, + textColor: Colors.white, + fontSize: 16.0, + ); + } +} \ No newline at end of file diff --git a/lib/widgets/awesome_dialog.dart b/lib/widgets/awesome_dialog.dart new file mode 100644 index 0000000..ee49904 --- /dev/null +++ b/lib/widgets/awesome_dialog.dart @@ -0,0 +1,54 @@ +import 'package:awesome_dialog/awesome_dialog.dart'; +import 'package:flutter/material.dart'; + +void showAwesomeDialog({ + required BuildContext context, + required Widget body, + required VoidCallback onOk, + required VoidCallback onCancel, +}) { + AwesomeDialog( + context: context, + dialogType: DialogType.noHeader, + animType: AnimType.scale, + body: body, + dialogBackgroundColor: Colors.white, + btnOkText: "确认", + btnCancelText: "取消", + btnOkColor: Colors.orange, + btnCancelColor: Colors.grey, + buttonsBorderRadius: BorderRadius.circular(10), + headerAnimationLoop: false, + dismissOnTouchOutside: false, + dismissOnBackKeyPress: true, + btnOk: ElevatedButton( + onPressed: onOk, + style: ElevatedButton.styleFrom( + elevation: 0, + backgroundColor: Colors.orange, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 6), + textStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + child: Text("确认"), + ), + btnCancelOnPress: onCancel, + ).show(); +} + +// 显示成功提示 +void showSuccessDialog(BuildContext context, String message) { + AwesomeDialog( + context: context, + dialogType: DialogType.success, + animType: AnimType.scale, + title: message, + btnOkText: "好的", + btnOkColor: Colors.green, + btnOkOnPress: () {}, + autoHide: Duration(seconds: 2), + ).show(); +} \ No newline at end of file diff --git a/lib/widgets/common.dart b/lib/widgets/common.dart index 2ac30a2..1d36ae9 100644 --- a/lib/widgets/common.dart +++ b/lib/widgets/common.dart @@ -1,4 +1,3 @@ -import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; BoxDecoration buildBoxDecoration() { @@ -11,6 +10,7 @@ BoxDecoration buildBoxDecoration() { Container buildBody({Widget? child}) { return Container( + width: double.infinity, color: Colors.grey[50], padding: EdgeInsets.all(8), child: child, @@ -24,28 +24,3 @@ Container buildCard({Widget? child}) { child: child, ); } - -void showAwesomeDialog({ - required BuildContext context, - required Widget body, - required VoidCallback onOk, - required VoidCallback onCancel, -}) { - AwesomeDialog( - context: context, - dialogType: DialogType.noHeader, - animType: AnimType.scale, - body: body, - dialogBackgroundColor: Colors.white, - btnOkText: "确认", - btnCancelText: "取消", - btnOkColor: Colors.orange, - btnCancelColor: Colors.grey, - buttonsBorderRadius: BorderRadius.circular(10), - headerAnimationLoop: false, - dismissOnTouchOutside: false, - dismissOnBackKeyPress: true, - btnOkOnPress: onOk, - btnCancelOnPress: onCancel, - ).show(); -} diff --git a/lib/widgets/todo_dialog.dart b/lib/widgets/todo_dialog.dart deleted file mode 100644 index d3aedbf..0000000 --- a/lib/widgets/todo_dialog.dart +++ /dev/null @@ -1,271 +0,0 @@ -import 'package:flisp_app/store/todo_dialog_store.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:flisp_app/models/todo.dart'; -import 'package:flisp_app/utils/date_utils.dart'; -import 'package:flisp_app/widgets/common.dart'; - -class TodoDialog extends StatefulWidget { - final bool isEditing; - final TodoItem? initialTodo; - - const TodoDialog({ - super.key, - required this.isEditing, - this.initialTodo, - }); - - @override - State createState() => _TodoDialogState(); -} - -class _TodoDialogState extends State { - late TextEditingController _titleController; - late TextEditingController _descriptionController; - - @override - void initState() { - super.initState(); - - _titleController = TextEditingController(); - _descriptionController = TextEditingController(); - - // 如果是编辑模式,初始化数据 - if (widget.isEditing && widget.initialTodo != null) { - WidgetsBinding.instance.addPostFrameCallback((_) { - final store = Provider.of(context, listen: false); - store.initEditData(widget.initialTodo!); - }); - } - } - - @override - void dispose() { - _titleController.dispose(); - _descriptionController.dispose(); - super.dispose(); - } - - Future _selectDueDate(BuildContext context) async { - final DateTime? picked = await showDatePicker( - context: context, - initialDate: DateTime.now(), - firstDate: DateTime.now(), - lastDate: DateTime(2100), - ); - - if (picked != null) { - final store = Provider.of(context, listen: false); - store.dueDate = picked; - } - } - - @override - Widget build(BuildContext context) { - final store = Provider.of(context); - - // 同步控制器文本 - if (_titleController.text != store.title) { - _titleController.text = store.title; - _titleController.selection = TextSelection.collapsed(offset: store.title.length); - } - - if (_descriptionController.text != store.description) { - _descriptionController.text = store.description; - _descriptionController.selection = TextSelection.collapsed(offset: store.description.length); - } - - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // 标题 - Row( - children: [ - Icon(Icons.add_task, color: Colors.orange, size: 24), - SizedBox(width: 8), - Text( - widget.isEditing ? '编辑待办事项' : '添加待办事项', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.orange, - ), - ), - ], - ), - SizedBox(height: 20), - - // 标题输入框 - TextField( - controller: _titleController, - onChanged: (value) => store.title = value, - decoration: InputDecoration( - labelText: '标题', - hintText: '请输入待办事项标题...', - counterText: '', - suffixText: '${store.title.length}/20', - suffixStyle: TextStyle( - color: store.title.length > 20 ? Colors.red : Colors.grey, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.grey.shade200), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.grey.shade200), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.orange, width: 1), - ), - filled: true, - fillColor: Colors.white, - prefixIcon: Icon(Icons.title, color: Colors.black87), - contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), - ), - style: TextStyle(fontSize: 16), - maxLength: 20, - ), - - SizedBox(height: 12), - - // 描述输入框 - TextField( - controller: _descriptionController, - onChanged: (value) => store.description = value, - decoration: InputDecoration( - labelText: '内容', - hintText: '请输入待办事项内容...', - counterText: '', - suffixText: '${store.description.length}/50', - suffixStyle: TextStyle( - color: store.description.length > 50 ? Colors.red : Colors.grey, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.grey.shade200), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.grey.shade200), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.orange, width: 1), - ), - filled: true, - fillColor: Colors.white, - prefixIcon: Icon(Icons.description, color: Colors.black87), - contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), - ), - style: TextStyle(fontSize: 16), - maxLines: 2, - maxLength: 50, - ), - - SizedBox(height: 12), - - // 截止日期选择 - Container( - decoration: buildBoxDecoration(), - child: ListTile( - leading: Icon(Icons.calendar_today, color: Colors.blue), - title: Text( - store.dueDate == null - ? '选择截止日期' - : '截止: ${formatDate(store.dueDate!)}', - style: TextStyle( - color: store.dueDate == null ? Colors.grey : Colors.black87, - ), - ), - trailing: store.dueDate != null - ? IconButton( - icon: Icon(Icons.clear, size: 18), - onPressed: () { - store.dueDate = null; - }, - ) - : null, - onTap: () => _selectDueDate(context), - ), - ), - SizedBox(height: 12), - - // 优先级选择 - Container( - decoration: buildBoxDecoration(), - child: ListTile( - leading: Container( - padding: EdgeInsets.all(6), - decoration: BoxDecoration( - color: store.priority.color.withAlpha(10), - shape: BoxShape.circle, - ), - child: Icon( - Icons.flag, - color: store.priority.color, - size: 18, - ), - ), - title: Text( - '优先级', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.grey.shade700, - ), - ), - trailing: DropdownButton( - value: store.priority, - underline: SizedBox(), - icon: Icon(Icons.arrow_drop_down, color: Colors.grey.shade600), - iconSize: 20, - dropdownColor: Colors.white, - borderRadius: BorderRadius.circular(12), - onChanged: (value) { - if (value != null) { - store.priority = value; - } - }, - items: TodoPriority.values.map((priority) { - return DropdownMenuItem( - value: priority, - child: Container( - padding: EdgeInsets.symmetric(vertical: 8), - child: Row( - children: [ - Container( - padding: EdgeInsets.all(4), - decoration: buildBoxDecoration(), - child: Icon( - priority.icon, - color: priority.color, - size: 14, - ), - ), - SizedBox(width: 12), - Text( - priority.label, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ); - }).toList(), - ), - contentPadding: EdgeInsets.symmetric(horizontal: 16), - minLeadingWidth: 0, - ), - ), - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/widgets/todo_form.dart b/lib/widgets/todo_form.dart new file mode 100644 index 0000000..6205b62 --- /dev/null +++ b/lib/widgets/todo_form.dart @@ -0,0 +1,292 @@ +import 'package:flisp_app/provider/todo_provider.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:provider/provider.dart'; +import 'package:flisp_app/models/todo.dart'; +import 'package:flisp_app/utils/date_utils.dart'; + +class TodoForm extends StatefulWidget { + final GlobalKey formKey; + final bool isEditing; + final TodoItem? initialTodo; + + const TodoForm({ + super.key, + required this.formKey, + required this.isEditing, + this.initialTodo, + }); + + @override + State createState() => _TodoFormState(); +} + +class _TodoFormState extends State { + @override + void initState() { + super.initState(); + + // 如果是编辑模式,初始化数据 + if (widget.isEditing && widget.initialTodo != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + final store = Provider.of(context, listen: false); + store.initForm(widget.initialTodo!); + }); + } + } + + @override + Widget build(BuildContext context) { + final int maxTitleCount = 10; + final int maxContentCount = 20; + final store = Provider.of(context); + + Widget buildTitle() { + return Row( + children: [ + Icon( + widget.isEditing ? Icons.edit_note : Icons.add_task, + color: Colors.orange, + size: 24, + ), + SizedBox(width: 8), + Text( + widget.isEditing ? '编辑待办事项' : '添加待办事项', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.orange, + ), + ), + ], + ); + } + + FormBuilderTextField buildTitleField() { + return FormBuilderTextField( + name: 'title', + initialValue: store.formItem.title, + onChanged: (value) { + setState(() { + store.formItem.title = value ?? ''; + }); + }, + decoration: InputDecoration( + labelText: '标题', + hintText: '请输入待办事项标题...', + counterText: '', + suffixText: '${store.formItem.title.length}/$maxTitleCount', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade200), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade200), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.orange), + ), + filled: true, + fillColor: Colors.white, + prefixIcon: Icon(Icons.title, color: Colors.blue), + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), + ), + maxLength: maxTitleCount, + validator: (value) { + if (value == null || value.isEmpty) { + return '请输入标题'; + } + if (value.length > maxTitleCount) { + return '标题不能超过$maxTitleCount个字符'; + } + return null; + }, + ); + } + + FormBuilderTextField buildDescriptionField() { + return FormBuilderTextField( + name: 'description', + initialValue: store.formItem.content, + onChanged: (value) { + setState(() { + store.formItem.content = value ?? ''; + }); + }, + decoration: InputDecoration( + labelText: '内容', + hintText: '请输入待办事项内容...', + counterText: '', + suffixText: '${store.formItem.content.length}/$maxContentCount', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade200), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade200), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.orange), + ), + filled: true, + fillColor: Colors.white, + prefixIcon: Icon(Icons.description, color: Colors.green), + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), + ), + maxLines: 2, + maxLength: maxContentCount, + validator: (value) { + if (value != null && value.length > maxContentCount) { + return '内容不能超过$maxContentCount个字符'; + } + return null; + }, + ); + } + + IconButton buildClearDateSuffixIcon() { + return IconButton( + icon: Icon(Icons.clear, size: 18), + onPressed: () { + setState(() { + store.formItem.dueDate = null; + }); + widget.formKey.currentState?.fields['dueDate']?.didChange(null); + }, + ); + } + + FormBuilderDateTimePicker buildDateField() { + return FormBuilderDateTimePicker( + name: 'dueDate', + initialValue: store.formItem.dueDate, + inputType: InputType.date, + onChanged: (value) { + setState(() { + store.formItem.dueDate = value; + }); + }, + decoration: InputDecoration( + labelText: + store.formItem.dueDate == null + ? '选择截止日期' + : '截止: ${formatDate(store.formItem.dueDate!)}', + labelStyle: TextStyle( + color: + store.formItem.dueDate == null ? Colors.grey : Colors.black87, + ), + prefixIcon: Icon(Icons.calendar_today, color: Colors.purple), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade200), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey.shade200), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.orange), + ), + filled: true, + fillColor: Colors.white, + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), + suffixIcon: + store.formItem.dueDate != null + ? buildClearDateSuffixIcon() + : null, + ), + validator: (value) { + if (value != null && value.isBefore(DateTime.now())) { + return '不能选择过去的日期'; + } + return null; + }, + ); + } + + FormBuilderRadioGroup builderRadioGroup() { + return FormBuilderRadioGroup( + name: 'priority', + initialValue: store.formItem.priority, + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + ), + orientation: OptionsOrientation.horizontal, + wrapSpacing: 6, + options: + TodoPriority.values.map((priority) { + return FormBuilderFieldOption( + value: priority, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [Text(priority.label)], + ), + ); + }).toList(), + onChanged: (value) { + if (value != null) { + setState(() { + store.formItem.priority = value; + }); + } + }, + ); + } + + Container buildPriorityField() { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade300, width: 1), + ), + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(Icons.flag, color: Colors.orange), + SizedBox(width: 6), + Text('优先级'), + ], + ), + builderRadioGroup(), + ], + ), + ); + } + + FormBuilder buildForm() { + return FormBuilder( + key: widget.formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + buildTitleField(), + SizedBox(height: 12), + buildDescriptionField(), + SizedBox(height: 12), + buildDateField(), + SizedBox(height: 12), + buildPriorityField(), + ], + ), + ); + } + + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [buildTitle(), SizedBox(height: 20), buildForm()], + ), + ); + } +} diff --git a/lib/widgets/todo_widget.dart b/lib/widgets/todo_widget.dart index 93dd613..3821be2 100644 --- a/lib/widgets/todo_widget.dart +++ b/lib/widgets/todo_widget.dart @@ -72,7 +72,7 @@ Widget buildTabs({ Widget buildTodoList({ required List todos, required ValueChanged onToggleTodo, - required ValueChanged onEditTodo + required ValueChanged onEditTodo, }) { return ListView.separated( itemCount: todos.length, @@ -82,7 +82,7 @@ Widget buildTodoList({ return _buildTodoItem( todo: todo, onToggle: onToggleTodo, - onEdit: onEditTodo + onEdit: onEditTodo, ); }, ); @@ -91,13 +91,18 @@ Widget buildTodoList({ Widget _buildTodoItem({ required TodoItem todo, required ValueChanged onToggle, - required ValueChanged onEdit + required ValueChanged onEdit, }) { return buildCard( child: ListTile( - leading: Checkbox( - value: todo.isCompleted, - onChanged: (value) => onToggle(todo.id), + contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 0), + leading: SizedBox( + width: 24, + child: Checkbox( + value: todo.isCompleted, + onChanged: (value) => onToggle(todo.id.toString()), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), ), title: _buildTodoTitle(todo), subtitle: _buildTodoSubtitle(todo), @@ -126,8 +131,7 @@ Widget? _buildTodoSubtitle(TodoItem todo) { todo.dueDate!.isBefore(DateTime.now()) && !todo.isCompleted; - final hasContent = - todo.description?.isNotEmpty == true || todo.dueDate != null; + final hasContent = todo.content.isNotEmpty == true || todo.dueDate != null; if (!hasContent) return null; @@ -135,9 +139,9 @@ Widget? _buildTodoSubtitle(TodoItem todo) { direction: Axis.vertical, spacing: 5, children: [ - if (todo.description?.isNotEmpty == true) + if (todo.content.isNotEmpty == true) Text( - todo.description!, + todo.content, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 12, color: Colors.grey.shade600), @@ -160,11 +164,7 @@ Widget _buildPriorityBadge(TodoPriority priority) { return Chip( label: Text(priority.label), backgroundColor: priority.color, - labelStyle: TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - ), + labelStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: Colors.white), @@ -175,7 +175,7 @@ Widget _buildPriorityBadge(TodoPriority priority) { } // 空状态 -Widget buildEmptyState(TodoTab currentFilter) { +Widget buildEmptyState(TodoTab tab) { final messages = { TodoTab.all: '📝 还没有待办事项\n点击➕号添加第一个任务吧~', TodoTab.active: '🎯 没有待完成的任务\n享受轻松时光吧!✨', @@ -188,11 +188,10 @@ Widget buildEmptyState(TodoTab currentFilter) { mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.checklist, size: 80, color: Colors.orange.shade600), - const SizedBox(height: 20), Text( - messages[currentFilter] ?? '暂无数据', + messages[tab] ?? '暂无数据', textAlign: TextAlign.center, - style: TextStyle(fontSize: 16, color: Colors.orange.shade600), + style: TextStyle(color: Colors.orange.shade600), ), ], ), diff --git a/pubspec.lock b/pubspec.lock index 4b19811..edf85b7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,6 +94,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_form_builder: + dependency: "direct main" + description: + name: flutter_form_builder + sha256: aa3901466c70b69ae6c7f3d03fcbccaec5fde179d3fded0b10203144b546ad28 + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.1" flutter_lints: dependency: "direct dev" description: @@ -102,14 +110,11 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.0.0" - flutter_riverpod: + flutter_localizations: dependency: "direct main" - description: - name: flutter_riverpod - sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.6.1" + description: flutter + source: sdk + version: "0.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -120,6 +125,22 @@ packages: description: flutter source: sdk version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + sha256: "90778fe0497fe3a09166e8cf2e0867310ff434b794526589e77ec03cf08ba8e8" + url: "https://pub.flutter-io.cn" + source: hosted + version: "8.2.14" + form_builder_validators: + dependency: "direct main" + description: + name: form_builder_validators + sha256: "475853a177bfc832ec12551f752fd0001278358a6d42d2364681ff15f48f67cf" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.1" graphs: dependency: transitive description: @@ -144,6 +165,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.19.0" leak_tracker: dependency: transitive description: @@ -280,14 +309,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.0.16" - riverpod: - dependency: transitive - description: - name: riverpod - sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.6.1" shared_preferences: dependency: "direct main" description: @@ -365,14 +386,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.12.1" - state_notifier: - dependency: transitive - description: - name: state_notifier - sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.0" stream_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 63c07c3..9b9e695 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,94 +1 @@ -name: flisp_app -description: "闪灵" -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -# In Windows, build-name is used as the major, minor, and patch parts -# of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+1 - -environment: - sdk: ^3.7.0 - -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. -dependencies: - flutter: - sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.8 - provider: ^6.1.1 # 状态管理 - shared_preferences: ^2.2.2 # 本地存储 - awesome_dialog: ^3.3.0 - toggle_switch: ^2.3.0 - flutter_riverpod: ^2.4.9 - -dev_dependencies: - flutter_test: - sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^5.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/to/asset-from-package - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/to/font-from-package +name: flisp_app description: "闪灵" # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # In Android, build-name is used as versionName while build-number used as versionCode. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. version: 1.0.0+1 environment: sdk: ^3.7.0 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions # consider running `flutter pub upgrade --major-versions`. Alternatively, # dependencies can be manually updated by changing the version numbers below to # the latest version available on pub.dev. To see which dependencies have newer # versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 provider: ^6.1.1 # 状态管理 shared_preferences: ^2.2.2 # 本地存储 awesome_dialog: ^3.3.0 toggle_switch: ^2.3.0 fluttertoast: ^8.2.2 flutter_form_builder: ^10.0.0 form_builder_validators: ^10.0.0 intl: ^0.19.0 dev_dependencies: flutter_test: sdk: flutter # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^5.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images # For details regarding adding assets from package dependencies, see # https://flutter.dev/to/asset-from-package # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/to/font-from-package \ No newline at end of file