feat:更新TabUI模块

This commit is contained in:
2025-11-18 11:46:21 +08:00
parent 21ff0ad94f
commit df26790389
9 changed files with 88 additions and 47 deletions

View File

@@ -1,9 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:food_hub_app/widgets/common/index.dart';
import 'package:food_hub_app/widgets/recipe/calendar.dart'; import 'package:food_hub_app/widgets/recipe/calendar.dart';
import 'package:food_hub_app/widgets/recipe/list.dart'; import 'package:food_hub_app/widgets/recipe/list.dart';
import 'package:food_hub_app/widgets/recipe/timeline.dart'; import 'package:food_hub_app/widgets/recipe/timeline.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
enum RecordTab {
recipe('菜谱'),
calendar('日历'),
timeline('时间轴');
final String label;
const RecordTab(this.label);
}
class RecordPage extends StatefulWidget { class RecordPage extends StatefulWidget {
const RecordPage({super.key}); const RecordPage({super.key});
@@ -12,51 +21,46 @@ class RecordPage extends StatefulWidget {
State<StatefulWidget> createState() => _RecordPageState(); State<StatefulWidget> createState() => _RecordPageState();
} }
class _RecordPageState extends State<RecordPage> class _RecordPageState extends State<RecordPage> {
with SingleTickerProviderStateMixin { RecordTab _currentTab = RecordTab.recipe;
final List<Widget> _tabPages = [
late final TabController _tabController = TabController( RecipeList(),
length: 3, RecipeCalendar(),
vsync: this, RecipeTimeline(),
);
final List<TDTab> tabs = [
const TDTab(text: '菜谱', icon: Icon(Icons.book)),
const TDTab(text: '日历', icon: Icon(Icons.calendar_month)),
const TDTab(text: '时间轴', icon: Icon(Icons.timeline)),
]; ];
@override Widget buildTabs({
void initState() { required BuildContext context,
super.initState(); required RecordTab currentTab,
} required ValueChanged<RecordTab> onTabChanged,
}) {
@override return Container(
void dispose() { padding: EdgeInsets.all(8),
_tabController.dispose(); child: buildToggleSwitch(
super.dispose(); context: context,
currentTab: currentTab,
tabValues: RecordTab.values,
labels: RecordTab.values.map((e) => e.label).toList(),
onTabChanged: onTabChanged,
),
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
children: [ children: [
TDTabBar( buildTabs(
tabs: tabs, context: context,
controller: _tabController, currentTab: _currentTab,
backgroundColor: Colors.white, onTabChanged: (tab) {
showIndicator: true, setState(() {
_currentTab = tab;
});
},
), ),
SizedBox(height: 8),
Expanded( Expanded(
child: TabBarView( child: IndexedStack(index: _currentTab.index, children: _tabPages),
controller: _tabController,
children: [
RecipeList(),
RecipeCalendar(),
RecipeTimeline(),
],
),
), ),
], ],
); );

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:toggle_switch/toggle_switch.dart';
Widget formLabelText({required String labelText, bool isRequired = false}) { Widget formLabelText({required String labelText, bool isRequired = false}) {
return Text.rich( return Text.rich(
@@ -50,16 +51,46 @@ Widget buildEmptyData() {
); );
} }
Widget buildToggleSwitch<T extends Enum>({
required BuildContext context,
required T currentTab,
required List<T> tabValues,
required List<String> labels,
required ValueChanged<T> onTabChanged,
}) {
return ToggleSwitch(
minWidth: 90.0,
minHeight: 40.0,
initialLabelIndex: tabValues.indexOf(currentTab),
totalSwitches: tabValues.length,
labels: labels,
activeBgColor: [Theme.of(context).colorScheme.primary],
activeFgColor: Colors.white,
inactiveBgColor: Colors.grey.shade200,
inactiveFgColor: Colors.grey.shade700,
cornerRadius: 12.0,
customTextStyles: [TextStyle(fontSize: 12, fontWeight: FontWeight.w500)],
onToggle: (index) {
if (index != null && index < tabValues.length) {
onTabChanged(tabValues[index]);
}
},
);
}
/// 圆形图标按钮 /// 圆形图标按钮
Widget circleIconButton({ Widget circleIconButton({
required IconData icon, required IconData icon,
required VoidCallback onPressed, required VoidCallback onPressed,
required BuildContext context, required BuildContext context,
bool? isSmall
}) { }) {
final double size = isSmall == true ? 24 : 36;
return ElevatedButton( return ElevatedButton(
onPressed: onPressed, onPressed: onPressed,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
fixedSize: Size(26, 26), fixedSize: Size(size, size),
shape: CircleBorder(), shape: CircleBorder(),
elevation: 0, elevation: 0,
backgroundColor: Theme.of(context).colorScheme.primary, backgroundColor: Theme.of(context).colorScheme.primary,

View File

@@ -91,6 +91,7 @@ class _YearSelectorState extends State<YearSelector>
circleIconButton( circleIconButton(
context: context, context: context,
icon: Icons.chevron_left, icon: Icons.chevron_left,
isSmall: true,
onPressed: () => _previousYear(), onPressed: () => _previousYear(),
), ),
SizedBox(width: 5), SizedBox(width: 5),
@@ -113,6 +114,7 @@ class _YearSelectorState extends State<YearSelector>
circleIconButton( circleIconButton(
context: context, context: context,
icon: Icons.chevron_right, icon: Icons.chevron_right,
isSmall: true,
onPressed: () => _nextYear(), onPressed: () => _nextYear(),
) )
], ],

View File

@@ -114,13 +114,13 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
return Card( return Card(
elevation: 0, elevation: 0,
color: colors.inversePrimary, color: colors.onSecondary,
child: Padding( child: Padding(
padding: EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: Row( child: Row(
children: [ children: [
CircleAvatar( CircleAvatar(
radius: 24, radius: 20,
backgroundColor: colors.primary, backgroundColor: colors.primary,
child: Text( child: Text(
record.category[0], record.category[0],

View File

@@ -39,7 +39,7 @@ class RecipeCard extends StatelessWidget {
icon: Icons.edit, icon: Icons.edit,
onPressed: () => {}, onPressed: () => {},
), ),
SizedBox(width: 8), SizedBox(width: 4),
circleIconButton( circleIconButton(
context: context, context: context,
icon: Icons.book, icon: Icons.book,
@@ -54,7 +54,6 @@ class RecipeCard extends StatelessWidget {
), ),
], ],
), ),
const SizedBox(height: 5),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,

View File

@@ -33,14 +33,11 @@ class _RecipeListState extends State<RecipeList> {
if (recipeSummaryList.isEmpty) { if (recipeSummaryList.isEmpty) {
return buildEmptyData(); return buildEmptyData();
} else { } else {
return ListView.separated( return ListView.builder(
itemCount: recipeSummaryList.length, itemCount: recipeSummaryList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return RecipeCard(recipe: recipeSummaryList[index]); return RecipeCard(recipe: recipeSummaryList[index]);
}, }
separatorBuilder: (context, index) {
return SizedBox(height: 10);
},
); );
} }
} }

View File

@@ -5,7 +5,6 @@ import 'package:food_hub_app/models/recipe.dart';
import 'package:food_hub_app/widgets/common/image.dart'; import 'package:food_hub_app/widgets/common/image.dart';
import 'package:food_hub_app/widgets/common/index.dart'; import 'package:food_hub_app/widgets/common/index.dart';
import 'package:food_hub_app/widgets/common/year_selector.dart'; import 'package:food_hub_app/widgets/common/year_selector.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'package:timelines_plus/timelines_plus.dart'; import 'package:timelines_plus/timelines_plus.dart';
class RecipeTimeline extends StatefulWidget { class RecipeTimeline extends StatefulWidget {

View File

@@ -906,6 +906,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
toggle_switch:
dependency: "direct main"
description:
name: toggle_switch
sha256: dca04512d7c23ed320d6c5ede1211a404f177d54d353bf785b07d15546a86ce5
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.0"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:

View File

@@ -38,6 +38,7 @@ dependencies:
dio: ^5.7.0 dio: ^5.7.0
timelines_plus: ^1.0.7 timelines_plus: ^1.0.7
table_calendar: ^3.1.3 table_calendar: ^3.1.3
toggle_switch: ^2.3.0
flutter_form_builder: ^10.0.0 flutter_form_builder: ^10.0.0
form_builder_validators: ^11.1.2 form_builder_validators: ^11.1.2
intl: ^0.19.0 intl: ^0.19.0