From 190963f079301dd4a26f5fffa39105c39836beac Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Tue, 9 Dec 2025 10:19:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9B=B4=E6=96=B0=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/config/app_config.dart | 6 +- lib/main.dart | 5 - lib/utils/record_utils.dart | 38 +++++- lib/widget/dropdown_select.dart | 45 ------- lib/widget/form.dart | 157 +++++++++++++++++++++++ lib/widget/number_input.dart | 129 ------------------- lib/widget/record/record_daily.dart | 3 +- lib/widget/record/record_form.dart | 139 +++++++++----------- lib/widget/select_input.dart | 188 ---------------------------- lib/widget/time_picker.dart | 69 ---------- pubspec.lock | 16 +++ pubspec.yaml | 2 +- 12 files changed, 270 insertions(+), 527 deletions(-) delete mode 100644 lib/widget/dropdown_select.dart create mode 100644 lib/widget/form.dart delete mode 100644 lib/widget/number_input.dart delete mode 100644 lib/widget/select_input.dart delete mode 100644 lib/widget/time_picker.dart diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index a7e63cf..d413ac2 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -1,7 +1,7 @@ class AppConfig { - static const int initWeight = 72; - static const int goalWeight = 70; - static const int goalTotalSpot = 600; + static const int initWeight = 73; + static const int goalWeight = 72; + static const int goalTotalSpot = 400; static const int goalAvgSleep = 8; static const List sportProjectList = ['羽毛球', '乒乓球', '跑步']; diff --git a/lib/main.dart b/lib/main.dart index b411d4a..baf3b78 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -47,11 +47,6 @@ class MyApp extends StatelessWidget { child: child!, ); }, - theme: ThemeData( - fontFamily: 'Inter', - scaffoldBackgroundColor: const Color(0xFFF9FAFB), - primaryColor: const Color(0xFF38BDF8), - ), home: HomePage(), debugShowCheckedModeBanner: false, ); diff --git a/lib/utils/record_utils.dart b/lib/utils/record_utils.dart index 0fa89f5..1886bd9 100644 --- a/lib/utils/record_utils.dart +++ b/lib/utils/record_utils.dart @@ -8,14 +8,25 @@ bool checkHasRecordValue(HealthRecord record, RecordType type) { case RecordType.sport: return record.sportDuration != 0; case RecordType.sleep: - return record.sleepStartTime != ''; + return record.sleepStartTime != '' || record.sleepEndTime != ''; + } +} + +bool checkNeedRecordTip(HealthRecord record, RecordType type) { + switch (type) { + case RecordType.weight: + return record.weight != 0; + case RecordType.sport: + return record.sportDuration != 0; + case RecordType.sleep: + return record.sleepStartTime != '' && record.sleepEndTime != ''; } } // 将字符串时间转换为 TimeOfDay -TimeOfDay parseTime(String timeString) { +TimeOfDay? parseTime(String timeString) { if (timeString.isEmpty) { - return TimeOfDay.now(); + return null; } try { final parts = timeString.split(':'); @@ -25,8 +36,23 @@ TimeOfDay parseTime(String timeString) { } } +DateTime? timeOfDayToDateTime(TimeOfDay? timeOfDay) { + if (timeOfDay == null) { + return null; + } + + final now = DateTime.now(); + return DateTime( + now.year, + now.month, + now.day, + timeOfDay.hour, + timeOfDay.minute, + ); +} + // 将 TimeOfDay 格式化为字符串 (HH:mm) -String formatTime(TimeOfDay time) { +String formatTime(DateTime time) { return '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; } @@ -34,8 +60,8 @@ int calSleepMinDuration(String startTime, String endTime) { final start = parseTime(startTime); final end = parseTime(endTime); - int startMinutes = start.hour * 60 + start.minute; - int endMinutes = end.hour * 60 + end.minute; + int startMinutes = start!.hour * 60 + start.minute; + int endMinutes = end!.hour * 60 + end.minute; // 处理跨天情况(如果结束时间小于开始时间,认为是第二天) if (endMinutes < startMinutes) { diff --git a/lib/widget/dropdown_select.dart b/lib/widget/dropdown_select.dart deleted file mode 100644 index 0cff85c..0000000 --- a/lib/widget/dropdown_select.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:fitnote/widget/record/common.dart'; -import 'package:flutter/material.dart'; -import 'package:getwidget/getwidget.dart'; - -class DropdownSelect extends StatelessWidget { - final String value; - final ValueChanged onChanged; - final List items; - - const DropdownSelect({ - Key? key, - required this.value, - required this.onChanged, - required this.items, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - final double width = 150; - final double height = 36; - - return Container( - width: width, - decoration: buildBoxDecoration(context), - child: DropdownButtonHideUnderline( - child: GFDropdown( - itemHeight: height, - padding: const EdgeInsets.all(0), - borderRadius: BorderRadius.circular(8), - value: value, - onChanged: onChanged, - items: - items - .map( - (value) => DropdownMenuItem( - value: value, - child: Text(value, style: const TextStyle(fontSize: 16)), - ), - ) - .toList(), - ), - ), - ); - } -} diff --git a/lib/widget/form.dart b/lib/widget/form.dart new file mode 100644 index 0000000..17dfb3b --- /dev/null +++ b/lib/widget/form.dart @@ -0,0 +1,157 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:intl/intl.dart'; + +TextStyle _titleStyle() { + return TextStyle(fontSize: 16, fontWeight: FontWeight.w600); +} + +TextStyle _contentStyle() { + return TextStyle(fontSize: 16, fontWeight: FontWeight.w500); +} + +OutlineInputBorder _border(ColorScheme colors) { + return OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colors.onSurface.withAlpha(100), width: 1.5), + ); +} + +OutlineInputBorder _enabledBorder(ColorScheme colors) { + return OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colors.onSurface.withAlpha(100), width: 1.5), + ); +} + +OutlineInputBorder _focusedBorder(ColorScheme colors) { + return OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colors.primary, width: 2), + ); +} + +EdgeInsetsGeometry _containerPadding() { + return EdgeInsets.symmetric(horizontal: 16, vertical: 10); +} + +Widget buildFormTime({ + required BuildContext context, + required String name, + required String title, + required DateTime? initialValue, + required ValueChanged onChanged, + double width = 100, + String hintText = '请选择时间', +}) { + final colors = Theme.of(context).colorScheme; + final alphaColor = colors.onSurface.withAlpha(100); + + return Row( + children: [ + Container(width: width, child: Text(title, style: _titleStyle())), + const SizedBox(width: 8), + Expanded( + child: FormBuilderDateTimePicker( + name: name, + inputType: InputType.time, + initialTime: TimeOfDay.now(), + initialValue: initialValue, + format: DateFormat.Hm(), + onChanged: (value) => onChanged(value), + style: _contentStyle(), + decoration: InputDecoration( + hintText: hintText, + hintStyle: TextStyle(color: alphaColor), + prefixIcon: Icon(Icons.schedule_rounded, size: 24), + border: _border(colors), + enabledBorder: _enabledBorder(colors), + focusedBorder: _focusedBorder(colors), + filled: true, + fillColor: colors.surfaceContainer, + contentPadding: _containerPadding(), + ), + ), + ), + ], + ); +} + +Widget buildFormSlider({ + required BuildContext context, + required String name, + required String title, + required String label, + required double min, + required double max, + required double initialValue, + required int divisions, + required ValueChanged onChanged, + double width = 100, +}) { + final colors = Theme.of(context).colorScheme; + final alphaColor = colors.onSurface.withAlpha(100); + + return Row( + children: [ + Container(width: width, child: Text(title, style: _titleStyle())), + const SizedBox(width: 8), + + Expanded( + child: FormBuilderSlider( + name: name, + min: min, + max: max, + initialValue: initialValue, + divisions: divisions, + onChanged: (value) => onChanged(value), + label: label, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + ), + ), + ), + ], + ); +} + +Widget buildFormRadioGroup({ + required BuildContext context, + required String name, + required String title, + required String initialValue, + required List items, + required ValueChanged onChanged, + double width = 100, +}) { + final colors = Theme.of(context).colorScheme; + final alphaColor = colors.onSurface.withAlpha(100); + + return Row( + children: [ + Container(width: width, child: Text(title, style: _titleStyle())), + const SizedBox(width: 8), + + Expanded( + child: FormBuilderRadioGroup( + name: name, + options: + items + .map( + (item) => + FormBuilderFieldOption(value: item, child: Text(item)), + ) + .toList(), + initialValue: initialValue, + activeColor: colors.primary, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + ), + onChanged: (value) => onChanged(value), + ), + ), + ], + ); +} diff --git a/lib/widget/number_input.dart b/lib/widget/number_input.dart deleted file mode 100644 index aceb3af..0000000 --- a/lib/widget/number_input.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:fitnote/widget/record/common.dart'; -import 'package:flutter/material.dart'; - -class NumberInput extends StatefulWidget { - final num? value; - final ValueChanged? onChanged; - final double min; - final double max; - final double step; - final int precision; - final String unit; - - const NumberInput({ - Key? key, - this.value, - this.onChanged, - this.min = double.negativeInfinity, - this.max = double.infinity, - this.step = 1, - this.precision = 2, - this.unit = '' - }) : super(key: key); - - @override - State createState() => _NumberInputState(); -} - -class _NumberInputState extends State { - late TextEditingController _controller; - late num _currentValue; - - final Color disabledColor = Colors.grey.shade400; - final double width = 150; - final double height = 36; - - @override - void initState() { - super.initState(); - _currentValue = widget.value ?? 0.0; - _controller = TextEditingController(text: _formatValue(_currentValue)); - } - - @override - void didUpdateWidget(NumberInput oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.value != oldWidget.value && widget.value != _currentValue) { - _currentValue = widget.value ?? 0.0; - _controller.text = _formatValue(_currentValue); - } - } - - String _formatValue(num value) { - if (widget.precision == 0) return value.toInt().toString(); - return value.toStringAsFixed(widget.precision); - } - - void _handleValueChange(num newValue) { - num clampedValue = newValue.clamp(widget.min, widget.max); - if (widget.precision > 0) { - clampedValue = double.parse( - clampedValue.toStringAsFixed(widget.precision), - ); - } - - setState(() { - _currentValue = clampedValue; - _controller.text = _formatValue(clampedValue); - }); - widget.onChanged?.call(clampedValue); - } - - Widget _buildControlButton({ - required IconData icon, - required VoidCallback? onPressed, - required bool disabled, - }) { - final colors = Theme.of(context).colorScheme; - - return Container( - width: height, - height: height, - child: Material( - color: disabled ? disabledColor : colors.primary.withAlpha(100), - child: InkWell( - onTap: disabled ? null : onPressed, - borderRadius: BorderRadius.circular(4), - child: Icon(icon, size: 18, color: colors.onSurface), - ), - ), - ); - } - - @override - Widget build(BuildContext context) { - return Container( - width: width, - decoration: buildBoxDecoration(context), - child: IntrinsicWidth( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - _buildControlButton( - icon: Icons.remove, - onPressed: () => _handleValueChange(_currentValue - widget.step), - disabled: _currentValue <= widget.min, - ), - Expanded( - child: Text( - '${_currentValue} ${widget.unit}', - textAlign: TextAlign.center, - ), - ), - _buildControlButton( - icon: Icons.add, - onPressed: () => _handleValueChange(_currentValue + widget.step), - disabled: _currentValue >= widget.max, - ), - ], - ), - ), - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } -} diff --git a/lib/widget/record/record_daily.dart b/lib/widget/record/record_daily.dart index 1c8a133..145e321 100644 --- a/lib/widget/record/record_daily.dart +++ b/lib/widget/record/record_daily.dart @@ -108,7 +108,8 @@ class _RecordDailyState extends State { _buildRecordTitle(type), const SizedBox(height: 4), _buildRecordContent(provider.currentRecord, type), - if (checkHasRecordValue(provider.currentRecord, type)) + const SizedBox(height: 4), + if (checkNeedRecordTip(provider.currentRecord, type)) _buildRecordTip( provider.currentRecord, provider.selectedDate, diff --git a/lib/widget/record/record_form.dart b/lib/widget/record/record_form.dart index cc865a1..1bc0736 100644 --- a/lib/widget/record/record_form.dart +++ b/lib/widget/record/record_form.dart @@ -3,9 +3,7 @@ import 'package:fitnote/models/health.dart'; import 'package:fitnote/provider/health_provider.dart'; import 'package:fitnote/service/health_service.dart'; import 'package:fitnote/utils/record_utils.dart'; -import 'package:fitnote/widget/dropdown_select.dart'; -import 'package:fitnote/widget/number_input.dart'; -import 'package:fitnote/widget/time_picker.dart'; +import 'package:fitnote/widget/form.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -79,22 +77,18 @@ class _RecordFormState extends State { children: [ _buildFormTitle('记录体重'), SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildFiledTitle('体重:'), - SizedBox(width: 10), - NumberInput( - value: provider.formItem.weight, - min: 0, - max: 100, - step: 0.1, - unit: 'Kg', - onChanged: (value) => provider.updateWeight(value!), - ), - ], + buildFormSlider( + context: context, + name: 'slider', + title: '体重(Kg)', + label: provider.formItem.weight.toString(), + min: 70, + max: 75, + initialValue: 72, + divisions: 50, + onChanged: (value) => provider.updateWeight(value!), ), - SizedBox(height: 20), + SizedBox(height: 10), ], ); case RecordType.sport: @@ -103,81 +97,66 @@ class _RecordFormState extends State { children: [ _buildFormTitle('记录运动'), SizedBox(height: 24), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildFiledTitle('运动类型:'), - SizedBox(width: 10), - DropdownSelect( - value: provider.formItem.sportProject, - items: AppConfig.sportProjectList, - onChanged: (value) => provider.updateSportProject(value!), - ), - ], + buildFormRadioGroup( + context: context, + title: '运动类型', + name: 'sport', + initialValue: provider.formItem.sportProject, + items: AppConfig.sportProjectList, + onChanged: (value) => provider.updateSportProject(value!), ), - SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildFiledTitle('运动时长:'), - SizedBox(width: 10), - NumberInput( - value: provider.formItem.sportDuration, - min: 5, - max: 240, - step: 5, - unit: '分钟', - onChanged: (value) => provider.updateSportDuration(value!), - ), - ], + SizedBox(height: 10), + buildFormSlider( + context: context, + name: 'slider', + title: '时长(分钟)', + label: provider.formItem.sportDuration.toString(), + min: 0, + max: 120, + initialValue: 30, + divisions: 12, + onChanged: (value) => provider.updateSportDuration(value!), ), - SizedBox(height: 24), + SizedBox(height: 10), ], ); case RecordType.sleep: + final startTime = provider.formItem.sleepStartTime; + final endTime = provider.formItem.sleepEndTime; + return Column( mainAxisSize: MainAxisSize.min, children: [ _buildFormTitle('记录睡眠'), SizedBox(height: 24), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildFiledTitle('入睡时间:'), - SizedBox(width: 10), - TimePicker( - initialTime: parseTime(provider.formItem.sleepStartTime), - label: provider.formItem.sleepStartTime, - onTimeSelected: (time) { - provider.updateSleepStartTime(formatTime(time)); - final startTime = provider.formItem.sleepStartTime; - final endTime = provider.formItem.sleepEndTime; - calSleepDuration(startTime, endTime); - }, - ), - ], + buildFormTime( + context: context, + name: 'startTime', + title: '入睡时间', + initialValue: timeOfDayToDateTime(parseTime(startTime)), + onChanged: (time) { + provider.updateSleepStartTime(formatTime(time!)); + final startTime = provider.formItem.sleepStartTime; + final endTime = provider.formItem.sleepEndTime; + calSleepDuration(startTime, endTime); + }, ), - SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildFiledTitle('起床时间:'), - SizedBox(width: 10), - TimePicker( - initialTime: parseTime(provider.formItem.sleepEndTime), - label: provider.formItem.sleepEndTime, - onTimeSelected: (time) { - provider.updateSleepEndTime(formatTime(time)); - final startTime = provider.formItem.sleepStartTime; - final endTime = provider.formItem.sleepEndTime; - calSleepDuration(startTime, endTime); - }, - ), - ], + SizedBox(height: 10), + buildFormTime( + context: context, + name: 'endTime', + title: '起床时间', + initialValue: timeOfDayToDateTime(parseTime(endTime)), + onChanged: (time) { + provider.updateSleepEndTime(formatTime(time!)); + final startTime = provider.formItem.sleepStartTime; + final endTime = provider.formItem.sleepEndTime; + calSleepDuration(startTime, endTime); + }, ), - SizedBox(height: 20), + SizedBox(height: 10), _buildSleepDuration(provider.formItem), - SizedBox(height: 24), + SizedBox(height: 10), ], ); } diff --git a/lib/widget/select_input.dart b/lib/widget/select_input.dart deleted file mode 100644 index d0434a2..0000000 --- a/lib/widget/select_input.dart +++ /dev/null @@ -1,188 +0,0 @@ -import 'package:flutter/material.dart'; - -class SelectInputField extends StatefulWidget { - final List options; - final String? value; - final ValueChanged? onChanged; - final String hintText; - final String labelText; - final bool enabled; - final EdgeInsetsGeometry? padding; - final InputBorder? border; - - const SelectInputField({ - Key? key, - required this.options, - this.value, - this.onChanged, - this.hintText = '请选择或输入', - this.labelText = '', - this.enabled = true, - this.padding, - this.border, - }) : super(key: key); - - @override - State createState() => _SelectInputFieldState(); -} - -class _SelectInputFieldState extends State { - final TextEditingController _controller = TextEditingController(); - final FocusNode _focusNode = FocusNode(); - bool _showDropdown = false; - String? _selectedValue; - - @override - void initState() { - super.initState(); - _selectedValue = widget.value; - _controller.text = widget.value ?? ''; - } - - @override - void didUpdateWidget(SelectInputField oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.value != oldWidget.value) { - _selectedValue = widget.value; - _controller.text = widget.value ?? ''; - } - } - - void _onTextChanged(String text) { - setState(() { - _selectedValue = text.isEmpty ? null : text; - _showDropdown = text.isNotEmpty; - }); - widget.onChanged?.call(text.isEmpty ? null : text); - } - - void _onOptionSelected(String option) { - setState(() { - _selectedValue = option; - _controller.text = option; - _showDropdown = false; - }); - widget.onChanged?.call(option); - _focusNode.unfocus(); - } - - void _onFieldTap() { - setState(() { - _showDropdown = !_showDropdown; - }); - } - - void _onFocusChange(bool hasFocus) { - setState(() { - _showDropdown = hasFocus; - }); - } - - List get _filteredOptions { - if (_controller.text.isEmpty) { - return widget.options; - } - return widget.options - .where((option) => - option.toLowerCase().contains(_controller.text.toLowerCase())) - .toList(); - } - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.labelText.isNotEmpty) ...[ - Text( - widget.labelText, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.grey[700], - ), - ), - const SizedBox(height: 8), - ], - Stack( - children: [ - TextField( - controller: _controller, - focusNode: _focusNode, - onChanged: _onTextChanged, - onTap: _onFieldTap, - enabled: widget.enabled, - decoration: InputDecoration( - hintText: widget.hintText, - border: widget.border ?? const OutlineInputBorder(), - contentPadding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 14, - ), - suffixIcon: IconButton( - icon: Icon( - _showDropdown ? Icons.arrow_drop_up : Icons.arrow_drop_down, - color: Colors.grey[600], - ), - onPressed: () { - setState(() { - _showDropdown = !_showDropdown; - }); - if (_showDropdown) { - _focusNode.requestFocus(); - } else { - _focusNode.unfocus(); - } - }, - ), - ), - ), - if (_showDropdown && _filteredOptions.isNotEmpty) - Positioned( - top: 60, - left: 0, - right: 0, - child: _buildDropdownList(), - ), - ], - ), - ], - ); - } - - Widget _buildDropdownList() { - return Material( - elevation: 4, - borderRadius: BorderRadius.circular(8), - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.grey[300]!), - ), - constraints: const BoxConstraints(maxHeight: 200), - child: ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.zero, - itemCount: _filteredOptions.length, - itemBuilder: (context, index) { - final option = _filteredOptions[index]; - return ListTile( - title: Text(option), - onTap: () => _onOptionSelected(option), - dense: true, - visualDensity: VisualDensity.compact, - ); - }, - ), - ), - ); - } - - @override - void dispose() { - _controller.dispose(); - _focusNode.dispose(); - super.dispose(); - } -} \ No newline at end of file diff --git a/lib/widget/time_picker.dart b/lib/widget/time_picker.dart deleted file mode 100644 index 65fc9cf..0000000 --- a/lib/widget/time_picker.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:fitnote/widget/record/common.dart'; -import 'package:flutter/material.dart'; - -class TimePicker extends StatelessWidget { - final TimeOfDay initialTime; - final ValueChanged onTimeSelected; - final String? label; - - const TimePicker({ - super.key, - required this.initialTime, - required this.onTimeSelected, - this.label, - }); - - @override - Widget build(BuildContext context) { - final double width = 150; - final double height = 36; - - return Container( - width: width, - height: height, - decoration: buildBoxDecoration(context), - child: InkWell( - onTap: () => _showTimePicker(context), - borderRadius: BorderRadius.circular(8), - child: Container( - padding: const EdgeInsets.all(0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.access_time, size: 20, color: Colors.grey[600]), - const SizedBox(width: 12), - Text( - label ?? _formatTime(initialTime), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.grey[800], - ), - ), - const SizedBox(width: 8), - Icon(Icons.arrow_drop_down, size: 20, color: Colors.grey[500]), - ], - ), - ), - ), - ); - } - - Future _showTimePicker(BuildContext context) async { - final TimeOfDay? picked = await showTimePicker( - context: context, - initialTime: initialTime, - initialEntryMode: TimePickerEntryMode.dialOnly, - ); - - if (picked != null) { - onTimeSelected(picked); - } - } - - String _formatTime(TimeOfDay time) { - return '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; - } -} diff --git a/pubspec.lock b/pubspec.lock index bf14b38..57b0b0f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -282,6 +282,14 @@ packages: relative: true source: path version: "1.0.0+1" + 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_image_compress: dependency: transitive description: @@ -377,6 +385,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "10.9.1" + 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" frontend_server_client: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0f5fafe..385b0ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1 +1 @@ -name: fitnote description: "记录健康生活" publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: sdk: ^3.7.0 dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter cupertino_icons: ^1.0.8 getwidget: ^7.0.0 intl: ^0.19.0 font_awesome_flutter: ^10.7.0 hive: ^2.2.3 hive_flutter: ^1.1.0 numberpicker: ^2.1.2 provider: ^6.1.1 table_calendar: ^3.1.3 flutter_common: path: ..\flutter_common dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^5.0.0 hive_generator: ^2.0.1 build_runner: ^2.4.6 # The following section is specific to Flutter packages. flutter: uses-material-design: true \ No newline at end of file +name: fitnote description: "记录健康生活" publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: sdk: ^3.7.0 dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter cupertino_icons: ^1.0.8 getwidget: ^7.0.0 intl: ^0.19.0 font_awesome_flutter: ^10.7.0 hive: ^2.2.3 hive_flutter: ^1.1.0 numberpicker: ^2.1.2 provider: ^6.1.1 table_calendar: ^3.1.3 flutter_form_builder: ^10.0.0 form_builder_validators: ^10.0.0 flutter_common: path: ..\flutter_common dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^5.0.0 hive_generator: ^2.0.1 build_runner: ^2.4.6 # The following section is specific to Flutter packages. flutter: uses-material-design: true \ No newline at end of file