feat:增加加载中组件
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/apis/recipe.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/provider/food_provider.dart';
|
||||
import 'package:food_hub_app/utils/date_util.dart';
|
||||
import 'package:food_hub_app/utils/index.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
@@ -16,46 +14,7 @@ class RecipeCalendar extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||
DateTime _selectedDay = DateTime.now();
|
||||
DateTime _focusedDay = DateTime.now();
|
||||
|
||||
List<FoodRecord> recordList = [];
|
||||
List<FoodRecord> selectRecordList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_refreshRecord();
|
||||
}
|
||||
|
||||
Future<void> _refreshRecord() async {
|
||||
final result = await queryRecordApi(
|
||||
getFirstDayOfMonth(_focusedDay),
|
||||
getLastDayOfMonth(_focusedDay),
|
||||
);
|
||||
setState(() {
|
||||
recordList = result;
|
||||
|
||||
if (recordList.isNotEmpty) {
|
||||
_selectedDay = DateTime.parse(recordList.last.date);
|
||||
} else {
|
||||
_selectedDay = _focusedDay;
|
||||
}
|
||||
|
||||
_refreshSelectRecord();
|
||||
});
|
||||
}
|
||||
|
||||
void _refreshSelectRecord() {
|
||||
setState(() {
|
||||
selectRecordList =
|
||||
recordList
|
||||
.where((record) => record.date == formatDateTime(_selectedDay))
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
TableCalendar _recipeCalendar() {
|
||||
TableCalendar _recipeCalendar(FoodProvider provider) {
|
||||
return TableCalendar(
|
||||
locale: 'zh_CN',
|
||||
headerStyle: const HeaderStyle(
|
||||
@@ -64,47 +23,53 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||
),
|
||||
firstDay: DateTime.utc(2010, 1, 1),
|
||||
lastDay: DateTime.utc(2100, 12, 31),
|
||||
focusedDay: _focusedDay,
|
||||
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
|
||||
focusedDay: provider.focusedDay,
|
||||
selectedDayPredicate: (day) => isSameDay(provider.selectedDay, day),
|
||||
onDaySelected: (selectedDay, focusedDay) {
|
||||
if (!isSameDay(_selectedDay, selectedDay)) {
|
||||
if (!isSameDay(provider.selectedDay, selectedDay)) {
|
||||
setState(() {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = focusedDay;
|
||||
_refreshSelectRecord();
|
||||
provider.selectedDay = selectedDay;
|
||||
provider.focusedDay = focusedDay;
|
||||
provider.refreshSelectRecord();
|
||||
});
|
||||
}
|
||||
},
|
||||
onPageChanged: (focusedDay) {
|
||||
_focusedDay = focusedDay;
|
||||
_refreshRecord();
|
||||
provider.focusedDay = focusedDay;
|
||||
provider.refreshRecordList(null, null);
|
||||
},
|
||||
// 自定义日期单元格构建器
|
||||
calendarBuilders: CalendarBuilders(
|
||||
defaultBuilder:
|
||||
(context, day, focusedDay) => _buildDateWidget(day, false, false),
|
||||
(context, day, focusedDay) =>
|
||||
_buildDateItem(day, false, false, provider),
|
||||
selectedBuilder:
|
||||
(context, day, focusedDay) => _buildDateWidget(day, true, false),
|
||||
(context, day, focusedDay) =>
|
||||
_buildDateItem(day, true, false, provider),
|
||||
todayBuilder:
|
||||
(context, day, focusedDay) => _buildDateWidget(day, false, true),
|
||||
(context, day, focusedDay) =>
|
||||
_buildDateItem(day, false, true, provider),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _dailyItem(BuildContext context) {
|
||||
Widget _dailyItem(BuildContext context, FoodProvider provider) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(formatDateTime(_selectedDay, 'MM月dd日 EEEE')),
|
||||
if (selectRecordList.isEmpty)
|
||||
Text(formatDateTime(provider.selectedDay, 'MM月dd日 EEEE')),
|
||||
if (provider.selectRecordList.isEmpty)
|
||||
buildEmptyData()
|
||||
else
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: selectRecordList.length,
|
||||
itemCount: provider.selectRecordList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildRecordItem(context, selectRecordList[index]);
|
||||
return _buildRecordItem(
|
||||
context,
|
||||
provider.selectRecordList[index],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -170,9 +135,14 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDateWidget(DateTime day, bool isSelected, bool isToday) {
|
||||
Widget _buildDateItem(
|
||||
DateTime day,
|
||||
bool isSelected,
|
||||
bool isToday,
|
||||
FoodProvider provider,
|
||||
) {
|
||||
// 检查当前日期是否在需要显示红点的列表中
|
||||
bool shouldShowRedDot = recordList.any(
|
||||
bool shouldShowRedDot = provider.recordList.any(
|
||||
(item) => item.date == formatDateTime(day),
|
||||
);
|
||||
|
||||
@@ -214,15 +184,31 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget _buildContent(FoodProvider provider) {
|
||||
return Column(
|
||||
children: [
|
||||
buildCard(context: context, child: _recipeCalendar()),
|
||||
buildCard(context: context, child: _recipeCalendar(provider)),
|
||||
Expanded(
|
||||
child: buildCard(context: context, child: _dailyItem(context)),
|
||||
child: buildCard(
|
||||
context: context,
|
||||
child: _dailyItem(context, provider),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = context.watch<FoodProvider>();
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
if (provider.isLoading)
|
||||
buildLoadingIndicator(context)
|
||||
else
|
||||
_buildContent(provider),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/widgets/common/image.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
@@ -86,10 +84,7 @@ class RecipeCard extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildNetworkImage(
|
||||
context,
|
||||
'${AppConfig.imageBaseUrl}/$firstImageUrl',
|
||||
),
|
||||
buildNetworkImage(context, firstImageUrl),
|
||||
SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () => navigatorToRecipeDetail(context),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/apis/recipe.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/provider/food_provider.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:food_hub_app/widgets/recipe/card.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class RecipeList extends StatefulWidget {
|
||||
const RecipeList({super.key});
|
||||
@@ -12,33 +12,40 @@ class RecipeList extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RecipeListState extends State<RecipeList> {
|
||||
List<RecipeSummary> recipeSummaryList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refreshRecipeList();
|
||||
|
||||
// 初始化加载数据
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.read<FoodProvider>().refreshRecipeList();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> refreshRecipeList() async {
|
||||
final result = await queryRecipeApi(RecipeQuery(category: ""));
|
||||
|
||||
setState(() {
|
||||
recipeSummaryList = result;
|
||||
});
|
||||
Widget _buildContent(FoodProvider provider) {
|
||||
if (provider.recipeSummaryList.isEmpty) {
|
||||
return buildEmptyData();
|
||||
} else {
|
||||
return ListView.builder(
|
||||
itemCount: provider.recipeSummaryList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return RecipeCard(recipe: provider.recipeSummaryList[index]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (recipeSummaryList.isEmpty) {
|
||||
return buildEmptyData();
|
||||
} else {
|
||||
return ListView.builder(
|
||||
itemCount: recipeSummaryList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return RecipeCard(recipe: recipeSummaryList[index]);
|
||||
}
|
||||
);
|
||||
}
|
||||
final provider = context.watch<FoodProvider>();
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
if (provider.isLoading)
|
||||
buildLoadingIndicator(context)
|
||||
else
|
||||
_buildContent(provider),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/apis/recipe.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/provider/food_provider.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/year_selector.dart';
|
||||
import 'package:timelines_plus/timelines_plus.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class RecipeTimeline extends StatefulWidget {
|
||||
const RecipeTimeline({super.key});
|
||||
@@ -15,24 +15,7 @@ class RecipeTimeline extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RecipeTimeline extends State<RecipeTimeline> {
|
||||
List<FoodRecord> recordList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refreshRecord(DateTime.now().year);
|
||||
}
|
||||
|
||||
Future<void> refreshRecord(int year) async {
|
||||
final result = await queryRecordApi("$year-01-01", "$year-12-31");
|
||||
setState(() {
|
||||
recordList = result;
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildTimelineCard(BuildContext context, FoodRecord record) {
|
||||
final imageUrl = '${AppConfig.imageBaseUrl}${record.imageUrl}';
|
||||
|
||||
return buildCard(
|
||||
context: context,
|
||||
child: Column(
|
||||
@@ -58,7 +41,7 @@ class _RecipeTimeline extends State<RecipeTimeline> {
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
buildNetworkImage(context, imageUrl),
|
||||
buildNetworkImage(context, record.imageUrl),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -93,8 +76,7 @@ class _RecipeTimeline extends State<RecipeTimeline> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget _buildContent(FoodProvider provider) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -102,9 +84,25 @@ class _RecipeTimeline extends State<RecipeTimeline> {
|
||||
initialYear: DateTime.now().year,
|
||||
minYear: 2000,
|
||||
maxYear: 2100,
|
||||
onYearChanged: (year) => refreshRecord(year),
|
||||
onYearChanged:
|
||||
(year) =>
|
||||
provider.refreshRecordList("$year-01-01", "$year-12-31"),
|
||||
),
|
||||
Expanded(child: buildTimeline(context, recordList)),
|
||||
Expanded(child: buildTimeline(context, provider.recordList)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = context.watch<FoodProvider>();
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
if (provider.isLoading)
|
||||
buildLoadingIndicator(context)
|
||||
else
|
||||
_buildContent(provider),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user