feat:更新TabUI模块
This commit is contained in:
@@ -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(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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],
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user