feat:重构模块
This commit is contained in:
@@ -27,7 +27,7 @@ pluginManagement {
|
||||
plugins {
|
||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||
id("com.android.application") version "8.7.0" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
|
||||
}
|
||||
|
||||
include(":app")
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
/// 应用信息
|
||||
class AppConfig {
|
||||
// 网络配置
|
||||
// http://192.168.1.3:8100
|
||||
// http://14.103.235.151:81/food-service
|
||||
// static const String baseApiUrl = "http://14.103.235.151:81/food-service";
|
||||
// static const String baseApiUrl = "http://192.168.1.3:8100";
|
||||
static const String baseApiUrl = "https://cxx0822.iepose.cn/food-api";
|
||||
// static const String baseApiUrl = "http://192.168.1.4:8083";
|
||||
// static const String baseApiUrl = "http://192.168.1.103:8083";
|
||||
static const String rustfsIp = '14.103.235.151';
|
||||
static const String rustfsFileUrl = 'http://14.103.235.151:9100';
|
||||
static const String baseApiUrl = "https://cxx0822.s.3q.hair/food-api";
|
||||
static const String rustfsIp = '43.248.188.28';
|
||||
static const int rustfsPort = 23125;
|
||||
static const String rustfsAccessKey = 'sZuAg1WCo5i4kD936Tqh';
|
||||
static const String rustfsSecretKey = 'g2fEyk6bslGrHepBvjhu0OLNF4qCRziPJIx1KAS8';
|
||||
static const String rustfsFileUrl = 'http://$rustfsIp:$rustfsPort';
|
||||
static const String bucketName = 'food';
|
||||
static const String imageBaseUrl = '$rustfsFileUrl/$bucketName/';
|
||||
// static const String imageBaseUrl = '$baseApiUrl/';
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'menu.dart';
|
||||
|
||||
class AppNavbar extends StatefulWidget {
|
||||
final int currentPageIndex;
|
||||
final Function(int) onTapNavbarItem;
|
||||
|
||||
const AppNavbar({
|
||||
super.key,
|
||||
required this.currentPageIndex,
|
||||
required this.onTapNavbarItem,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => AppNavbarState();
|
||||
}
|
||||
|
||||
class AppNavbarState extends State<AppNavbar> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return BottomNavigationBar(
|
||||
currentIndex: widget.currentPageIndex,
|
||||
onTap: widget.onTapNavbarItem,
|
||||
items: bottomNavItems,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
selectedItemColor: colors.primary,
|
||||
unselectedItemColor: colors.onSurface.withAlpha(150),
|
||||
showSelectedLabels: true,
|
||||
showUnselectedLabels: true,
|
||||
backgroundColor: colors.surface,
|
||||
elevation: 8,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/models/layout.dart';
|
||||
import 'package:food_hub_app/views/moment.dart';
|
||||
import 'package:food_hub_app/views/profile.dart';
|
||||
import 'package:food_hub_app/views/record.dart';
|
||||
import 'package:food_hub_app/views/stats.dart';
|
||||
|
||||
final List<PageInfo> pages = [
|
||||
PageInfo(
|
||||
label: "记录",
|
||||
icon: Icons.home_outlined,
|
||||
activeIcon: Icons.home,
|
||||
page: RecordPage(),
|
||||
),
|
||||
PageInfo(
|
||||
label: "统计",
|
||||
icon: Icons.pie_chart_outline,
|
||||
activeIcon: Icons.pie_chart,
|
||||
page: StatsPage(),
|
||||
),
|
||||
PageInfo(
|
||||
label: "朋友圈",
|
||||
icon: Icons.group_outlined,
|
||||
activeIcon: Icons.group,
|
||||
page: MomentPage(),
|
||||
),
|
||||
PageInfo(
|
||||
label: "我的",
|
||||
icon: Icons.account_circle_outlined,
|
||||
activeIcon: Icons.account_circle,
|
||||
page: ProfilePage(),
|
||||
),
|
||||
];
|
||||
|
||||
final List<BottomNavigationBarItem> bottomNavItems =
|
||||
pages.map((page) {
|
||||
return BottomNavigationBarItem(icon: Icon(page.icon), label: page.label);
|
||||
}).toList();
|
||||
|
||||
final List<Widget> tabPages = pages.map((item) => item.page).toList();
|
||||
@@ -16,6 +16,7 @@ import 'package:food_hub_app/views/recipe_form.dart';
|
||||
import 'package:food_hub_app/views/record_form.dart';
|
||||
import 'package:food_hub_app/views/recipe_detail.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_setup.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
void main() async {
|
||||
@@ -23,17 +24,22 @@ void main() async {
|
||||
|
||||
FormBuilderLocalizations.delegate.load(const Locale('zh', 'CN'));
|
||||
await SPUtil.init();
|
||||
await initLogger();
|
||||
// await initLogger();
|
||||
|
||||
// 预编译 shader,防止首帧白闪
|
||||
await LiquidGlassWidgets.initialize();
|
||||
// wrap() 安装无障碍桥接、全局主题、自适应质量
|
||||
runApp(
|
||||
MultiProvider(
|
||||
LiquidGlassWidgets.wrap(
|
||||
child: MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(create: (context) => FoodProvider()),
|
||||
ChangeNotifierProvider(create: (context) => ThemeProvider()),
|
||||
ChangeNotifierProvider(create: (context) => UserProvider()),
|
||||
ChangeNotifierProvider(create: (context) => AppProvider()),
|
||||
],
|
||||
child: MyApp(),
|
||||
child: const MyApp(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -45,10 +51,52 @@ class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeProvider = context.watch<ThemeProvider>();
|
||||
final brightness = MediaQuery.of(context).platformBrightness;
|
||||
themeProvider.isDarkMode = brightness == Brightness.dark;
|
||||
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: themeProvider.currentThemeData,
|
||||
theme: ThemeData(
|
||||
scaffoldBackgroundColor: const Color(0xFFFAF6F0),
|
||||
colorScheme: ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: const Color(0xFFE89F71),
|
||||
secondary: const Color(0xFFE89F71),
|
||||
surface: const Color(0xFFFFFFFF),
|
||||
error: const Color(0xFFE76F51),
|
||||
|
||||
onPrimary: Colors.white,
|
||||
onSecondary: Colors.white,
|
||||
onSurface: const Color(0xFF3A332C),
|
||||
onError: Colors.white,
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: const Color(0xFF3A332C),
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: const Color(0xFF3A332C),
|
||||
),
|
||||
bodyLarge: TextStyle(
|
||||
fontSize: 15,
|
||||
color: const Color(0xFF3A332C),
|
||||
),
|
||||
bodyMedium: TextStyle(
|
||||
fontSize: 14,
|
||||
color: const Color(0xFF8C8379),
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
fontSize: 12,
|
||||
color: const Color(0xFF8C8379),
|
||||
),
|
||||
),
|
||||
fontFamily: 'CustomFont',
|
||||
useMaterial3: true,
|
||||
),
|
||||
supportedLocales: const [
|
||||
Locale('en', 'US'), // 英语
|
||||
Locale('zh', 'CN'), // 中文
|
||||
@@ -71,13 +119,13 @@ class MyApp extends StatelessWidget {
|
||||
home: LoginPage(),
|
||||
routes: {
|
||||
'/home': (context) => HomePage(),
|
||||
'/recordForm': (context) => RecordFormPage(),
|
||||
'/recordForm': (context) => RecordForm(),
|
||||
'/recipeForm': (context) => RecipeFormPage(),
|
||||
'/recipeDetail': (context) => RecipeDetailPage(),
|
||||
'/momentForm': (context) => MomentFormPage(),
|
||||
'/momentForm': (context) => MomentForm(),
|
||||
'/moment': (context) => MomentPage(),
|
||||
'/momentUser': (context) => MomentUserPage(),
|
||||
'/ProfileUser': (context) => ProfileUserPage()
|
||||
'/ProfileUser': (context) => ProfileUserPage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
43
lib/utils/minio_utils.dart
Normal file
43
lib/utils/minio_utils.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter_common/utils/file_utils.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
import 'package:minio/io.dart';
|
||||
import 'package:minio/minio.dart';
|
||||
|
||||
|
||||
class MinIOHelper {
|
||||
static final MinIOHelper _instance = MinIOHelper._internal();
|
||||
|
||||
factory MinIOHelper() => _instance;
|
||||
|
||||
final String ip = AppConfig.rustfsIp;
|
||||
|
||||
MinIOHelper._internal() {
|
||||
_minio = Minio(
|
||||
endPoint: AppConfig.rustfsIp,
|
||||
port: AppConfig.rustfsPort,
|
||||
accessKey: AppConfig.rustfsAccessKey,
|
||||
secretKey: AppConfig.rustfsSecretKey,
|
||||
useSSL: false,
|
||||
);
|
||||
}
|
||||
|
||||
late Minio _minio;
|
||||
|
||||
Future<String> uploadFile({
|
||||
required PlatformFile file,
|
||||
required String bucketName,
|
||||
Function(double)? onProgress,
|
||||
}) async {
|
||||
try {
|
||||
String hashName = await generateMD5HashName(file.path!);
|
||||
String fileName = '$hashName${getFileExtension(file.name)}';
|
||||
|
||||
await _minio.fPutObject(bucketName, fileName, file.path!);
|
||||
|
||||
return fileName;
|
||||
} catch (e) {
|
||||
throw Exception('文件上传失败: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/layout/app_actions.dart';
|
||||
import 'package:food_hub_app/layout/app_drawer.dart';
|
||||
import 'package:food_hub_app/layout/app_navbar.dart';
|
||||
import 'package:food_hub_app/layout/menu.dart';
|
||||
import 'package:food_hub_app/views/profile.dart';
|
||||
import 'package:food_hub_app/views/record.dart';
|
||||
import 'package:food_hub_app/views/stats.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:liquid_glass_widgets/widgets/shared/glass_page.dart';
|
||||
import 'package:liquid_glass_widgets/widgets/surfaces/glass_app_bar.dart';
|
||||
import 'package:liquid_glass_widgets/widgets/surfaces/glass_bottom_bar.dart';
|
||||
import 'package:liquid_glass_widgets/widgets/surfaces/glass_scaffold.dart';
|
||||
|
||||
import 'moment.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
@@ -14,38 +20,52 @@ class HomePage extends StatefulWidget {
|
||||
class _HomePage extends State<HomePage> {
|
||||
int _currentIndex = 0;
|
||||
|
||||
final _pages = [
|
||||
const RecordPage(),
|
||||
const StatsPage(),
|
||||
const MomentPage(),
|
||||
const ProfilePage(),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text('食光集', style: TextStyle(color: Colors.white)),
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
leading: Builder(
|
||||
builder: (context) {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.menu),
|
||||
color: Colors.white,
|
||||
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||
);
|
||||
},
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return GlassScaffold(
|
||||
statusBarStyle: GlassStatusBarStyle.auto,
|
||||
bottomBar: GlassBottomBar(
|
||||
settings: LiquidGlassSettings(
|
||||
glassColor: colors.surface
|
||||
),
|
||||
actions: [AppActions(pageIndex: _currentIndex)],
|
||||
selectedIndex: _currentIndex,
|
||||
onTabSelected: (i) => setState(() => _currentIndex = i),
|
||||
indicatorColor: colors.primary,
|
||||
selectedIconColor: colors.surface,
|
||||
unselectedIconColor: Color(0xFF000000),
|
||||
tabs: const [
|
||||
GlassBottomBarTab(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
activeIcon: Icon(Icons.home),
|
||||
label: '记录',
|
||||
),
|
||||
// backgroundColor: Color(0xFFF5F5F5),
|
||||
drawer: AppDrawer(),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: tabPages[_currentIndex],
|
||||
GlassBottomBarTab(
|
||||
icon: Icon(Icons.pie_chart_outline),
|
||||
activeIcon: Icon(Icons.pie_chart),
|
||||
label: '统计',
|
||||
),
|
||||
bottomNavigationBar: AppNavbar(
|
||||
currentPageIndex: _currentIndex,
|
||||
onTapNavbarItem: (index) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
});
|
||||
},
|
||||
GlassBottomBarTab(
|
||||
icon: Icon(Icons.group_outlined),
|
||||
activeIcon: Icon(Icons.group),
|
||||
label: '朋友圈',
|
||||
),
|
||||
GlassBottomBarTab(
|
||||
icon: Icon(Icons.account_circle_outlined),
|
||||
activeIcon: Icon(Icons.account_circle),
|
||||
label: '我的',
|
||||
),
|
||||
],
|
||||
),
|
||||
body: _pages[_currentIndex],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,12 +104,12 @@ class _LoginPage extends State<LoginPage> {
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(child: buildLoginForm()),
|
||||
Expanded(child: buildLoginForm(context)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50),
|
||||
child: Column(
|
||||
children: [
|
||||
buildOtherLoginText(),
|
||||
buildOtherLoginText(context),
|
||||
const SizedBox(height: 15),
|
||||
buildOtherMethod(context),
|
||||
const SizedBox(height: 20),
|
||||
@@ -131,29 +131,29 @@ class _LoginPage extends State<LoginPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildLoginForm() {
|
||||
Widget buildLoginForm(BuildContext context) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
children: [
|
||||
const SizedBox(height: kToolbarHeight),
|
||||
buildTitle(),
|
||||
const SizedBox(height: 60),
|
||||
buildUsernameTextField(),
|
||||
buildUsernameTextField(context),
|
||||
const SizedBox(height: 20),
|
||||
buildPasswordTextField(),
|
||||
buildPasswordTextField(context),
|
||||
const SizedBox(height: 10),
|
||||
buildRememberPasswordCheckbox(),
|
||||
buildRememberPasswordCheckbox(context),
|
||||
const SizedBox(height: 20),
|
||||
buildLoginButton(),
|
||||
buildLoginButton(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildUsernameTextField() {
|
||||
Widget buildUsernameTextField(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
formLabelText(labelText: "账号:"),
|
||||
Text("账号:", style: Theme.of(context).textTheme.bodyLarge),
|
||||
const SizedBox(height: 10),
|
||||
FormBuilderTextField(
|
||||
name: 'username',
|
||||
@@ -171,11 +171,11 @@ class _LoginPage extends State<LoginPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildPasswordTextField() {
|
||||
Widget buildPasswordTextField(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
formLabelText(labelText: "密码:"),
|
||||
Text("密码:", style: Theme.of(context).textTheme.bodyLarge),
|
||||
const SizedBox(height: 10),
|
||||
FormBuilderTextField(
|
||||
name: 'password',
|
||||
@@ -205,7 +205,7 @@ class _LoginPage extends State<LoginPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildRememberPasswordCheckbox() {
|
||||
Widget buildRememberPasswordCheckbox(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
@@ -218,12 +218,12 @@ class _LoginPage extends State<LoginPage> {
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text('记住密码', style: TextStyle(color: Colors.grey, fontSize: 14)),
|
||||
Text('记住密码', style: Theme.of(context).textTheme.bodyMedium),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildLoginButton() {
|
||||
Widget buildLoginButton(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 45,
|
||||
width: double.infinity,
|
||||
@@ -243,23 +243,20 @@ class _LoginPage extends State<LoginPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildOtherLoginText() {
|
||||
Widget buildOtherLoginText(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(child: Divider(color: Colors.grey[300], thickness: 1)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
'其他方式登录',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 14),
|
||||
),
|
||||
child: Text('其他方式登录', style: Theme.of(context).textTheme.bodyMedium),
|
||||
),
|
||||
Expanded(child: Divider(color: Colors.grey[300], thickness: 1)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildOtherMethod(context) {
|
||||
Widget buildOtherMethod(BuildContext context) {
|
||||
return OverflowBar(
|
||||
alignment: MainAxisAlignment.center,
|
||||
children:
|
||||
@@ -292,16 +289,19 @@ class _LoginPage extends State<LoginPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildRegisterText(context) {
|
||||
Widget buildRegisterText(BuildContext context) {
|
||||
return Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text('没有账号?', style: TextStyle(fontSize: 18)),
|
||||
Text('没有账号?', style: Theme.of(context).textTheme.bodyMedium),
|
||||
GestureDetector(
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'点击注册',
|
||||
style: TextStyle(fontSize: 18, color: Colors.green),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
print("点击注册");
|
||||
|
||||
@@ -59,8 +59,8 @@ class _MomentPageState extends State<MomentPage> {
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
// 当滚动距离超过300时显示返回顶部按钮
|
||||
if (_scrollController.offset > 300) {
|
||||
// 当滚动距离超过时显示返回顶部按钮
|
||||
if (_scrollController.offset > 100) {
|
||||
if (!_showScrollToTop) {
|
||||
setState(() {
|
||||
_showScrollToTop = true;
|
||||
@@ -99,7 +99,17 @@ class _MomentPageState extends State<MomentPage> {
|
||||
}
|
||||
|
||||
// 有数据时显示列表
|
||||
return Stack(
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
title: Text('朋友圈', style: Theme.of(context).textTheme.titleLarge),
|
||||
centerTitle: true,
|
||||
automaticallyImplyLeading: false,
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsetsGeometry.all(10),
|
||||
child: Stack(
|
||||
children: [
|
||||
buildEasyRefresh(
|
||||
freshController: _freshController,
|
||||
@@ -119,6 +129,8 @@ class _MomentPageState extends State<MomentPage> {
|
||||
if (_showScrollToTop)
|
||||
buildScrollToTop(context: context, scrollToTop: _scrollToTop),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_common/utils/log_utils.dart';
|
||||
import 'package:flutter_common/utils/minio_utils.dart';
|
||||
import 'package:flutter_common/utils/toast_util.dart';
|
||||
import 'package:flutter_common/widget/common_widget.dart';
|
||||
import 'package:flutter_common/widget/dialog_widget.dart';
|
||||
import 'package:flutter_common/widget/loading_widget.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
import 'package:food_hub_app/provider/food_provider.dart';
|
||||
import 'package:food_hub_app/utils/minio_utils.dart';
|
||||
import 'package:food_hub_app/widgets/common/form.dart';
|
||||
import 'package:food_hub_app/widgets/common/image.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:liquid_glass_widgets/widgets/containers/glass_card.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MomentFormPage extends StatefulWidget {
|
||||
const MomentFormPage({super.key});
|
||||
class MomentForm extends StatefulWidget {
|
||||
const MomentForm({super.key});
|
||||
|
||||
@override
|
||||
State<MomentFormPage> createState() => _MomentFormPageState();
|
||||
State<MomentForm> createState() => _MomentFormState();
|
||||
}
|
||||
|
||||
class _MomentFormPageState extends State<MomentFormPage> {
|
||||
class _MomentFormState extends State<MomentForm> {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
final _focusNode = FocusNode();
|
||||
static const String _contentField = 'content';
|
||||
@@ -34,8 +35,8 @@ class _MomentFormPageState extends State<MomentFormPage> {
|
||||
name: _contentField,
|
||||
initialValue: provider.momentFormItem.content,
|
||||
focusNode: _focusNode,
|
||||
maxLines: 5,
|
||||
minLines: 3,
|
||||
maxLines: 8,
|
||||
minLines: 5,
|
||||
maxLength: maxContentLength,
|
||||
buildCounter: (
|
||||
context, {
|
||||
@@ -75,7 +76,7 @@ class _MomentFormPageState extends State<MomentFormPage> {
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisSpacing: 8,
|
||||
childAspectRatio: 4 / 3,
|
||||
@@ -111,7 +112,11 @@ class _MomentFormPageState extends State<MomentFormPage> {
|
||||
_buildContentField(provider),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
buildFormLabel(context: context, text: '上传图片(最多9张)', isRequired: false),
|
||||
buildFormLabel(
|
||||
context: context,
|
||||
text: '上传图片(最多9张)',
|
||||
isRequired: false,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildImageField(provider),
|
||||
const SizedBox(height: 8),
|
||||
@@ -146,11 +151,10 @@ class _MomentFormPageState extends State<MomentFormPage> {
|
||||
if (result != null) {
|
||||
// 限制选择数量
|
||||
final filesToUpload = result.files.take(remainingCount).toList();
|
||||
|
||||
LoadingDialog.show(context, message: '上传中');
|
||||
|
||||
try {
|
||||
final newImageUrls = <String>[];
|
||||
|
||||
// 逐个上传图片
|
||||
for (final file in filesToUpload) {
|
||||
final fileName = await MinIOHelper().uploadFile(
|
||||
@@ -162,8 +166,12 @@ class _MomentFormPageState extends State<MomentFormPage> {
|
||||
}
|
||||
|
||||
// 更新图片列表
|
||||
LoadingDialog.hide(context);
|
||||
provider.addMomentFormImage(newImageUrls);
|
||||
} catch (e) {
|
||||
ToastUtil.error(e.toString());
|
||||
} finally {
|
||||
LoadingDialog.hide(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,24 +213,17 @@ class _MomentFormPageState extends State<MomentFormPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final provider = context.watch<FoodProvider>();
|
||||
|
||||
final String title = provider.isEditing ? '编辑朋友圈' : '新增朋友圈';
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(title, style: const TextStyle(color: Colors.white)),
|
||||
backgroundColor: colors.primary,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: CommonCard(child: _buildFormBuilder(provider)),
|
||||
padding: EdgeInsets.all(10),
|
||||
child: GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(
|
||||
glassColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: _buildFormBuilder(provider),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -5,6 +5,8 @@ import 'package:flutter_common/widget/dialog_widget.dart';
|
||||
import 'package:food_hub_app/provider/app_provider.dart';
|
||||
import 'package:food_hub_app/provider/user_provider.dart';
|
||||
import 'package:food_hub_app/widgets/profile/basic_info.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:liquid_glass_widgets/widgets/containers/glass_card.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ProfilePage extends StatefulWidget {
|
||||
@@ -44,8 +46,13 @@ class ProfilePageState extends State<ProfilePage> {
|
||||
Navigator.pushNamed(context, '/momentUser');
|
||||
}
|
||||
|
||||
Widget _buildFunctionButtons() {
|
||||
return CommonCard(
|
||||
Widget _buildFunctionButtons(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return GlassCard(
|
||||
padding: EdgeInsetsGeometry.symmetric(vertical: 0, horizontal: 10),
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildFunctionButton(
|
||||
@@ -56,13 +63,13 @@ class ProfilePageState extends State<ProfilePage> {
|
||||
// LogUtils.i('点击编辑资料');
|
||||
},
|
||||
),
|
||||
const Divider(height: 1),
|
||||
const Divider(height: 1, thickness: 0.2),
|
||||
_buildFunctionButton(
|
||||
icon: Icons.group_outlined,
|
||||
text: '朋友圈',
|
||||
onTap: _onTapMoment,
|
||||
),
|
||||
const Divider(height: 1),
|
||||
const Divider(height: 1, thickness: 0.2),
|
||||
_buildFunctionButton(
|
||||
icon: Icons.favorite,
|
||||
text: '我的收藏',
|
||||
@@ -71,7 +78,7 @@ class ProfilePageState extends State<ProfilePage> {
|
||||
// LogUtils.i('点击浏览历史');
|
||||
},
|
||||
),
|
||||
const Divider(height: 1),
|
||||
const Divider(height: 1, thickness: 0.2),
|
||||
_buildFunctionButton(
|
||||
icon: Icons.lock,
|
||||
text: '更改密码',
|
||||
@@ -80,11 +87,11 @@ class ProfilePageState extends State<ProfilePage> {
|
||||
// LogUtils.i('点击帮助与反馈');
|
||||
},
|
||||
),
|
||||
const Divider(height: 1),
|
||||
const Divider(height: 1, thickness: 0.2),
|
||||
_buildFunctionButton(icon: Icons.message, text: '反馈意见', onTap: () {}),
|
||||
const Divider(height: 1),
|
||||
const Divider(height: 1, thickness: 0.2),
|
||||
_buildFunctionButton(icon: Icons.refresh, text: '检查更新', onTap: () {}),
|
||||
const Divider(height: 1),
|
||||
const Divider(height: 1, thickness: 0.2),
|
||||
_buildFunctionButton(
|
||||
icon: Icons.exit_to_app,
|
||||
text: '退出登录',
|
||||
@@ -117,13 +124,13 @@ class ProfilePageState extends State<ProfilePage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(UserProvider provider) {
|
||||
Widget _buildContent(UserProvider provider, BuildContext context) {
|
||||
final user = provider.currentUser;
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(width: double.infinity, child: ProfileInfo(user: user)),
|
||||
const SizedBox(height: 16),
|
||||
_buildFunctionButtons(),
|
||||
_buildFunctionButtons(context),
|
||||
const SizedBox(height: 8),
|
||||
_buildVersionInfo(),
|
||||
],
|
||||
@@ -134,15 +141,27 @@ class ProfilePageState extends State<ProfilePage> {
|
||||
Widget build(BuildContext context) {
|
||||
final provider = context.watch<UserProvider>();
|
||||
|
||||
return Stack(
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
title: Text('我的', style: Theme.of(context).textTheme.titleLarge),
|
||||
centerTitle: true,
|
||||
automaticallyImplyLeading: false,
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsetsGeometry.all(10),
|
||||
child: Stack(
|
||||
children: [
|
||||
if (provider.isLoading)
|
||||
buildLoadingIndicator()
|
||||
else if (provider.error.isNotEmpty)
|
||||
Text(provider.error)
|
||||
else
|
||||
_buildContent(provider),
|
||||
SingleChildScrollView(child: _buildContent(provider, context)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_common/widget/common_widget.dart';
|
||||
import 'package:food_hub_app/provider/food_provider.dart';
|
||||
import 'package:food_hub_app/views/moment_form.dart';
|
||||
import 'package:food_hub_app/views/record_form.dart';
|
||||
import 'package:food_hub_app/widgets/common/easy_refresh.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/list.dart';
|
||||
@@ -24,12 +29,68 @@ class RecordPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RecordPageState extends State<RecordPage> {
|
||||
final EasyRefreshController _freshController = EasyRefreshController(
|
||||
controlFinishRefresh: true,
|
||||
controlFinishLoad: true,
|
||||
);
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
bool _showScrollToTop = false;
|
||||
final List<Widget> _tabPages = [
|
||||
RecipeList(),
|
||||
RecipeCalendar(),
|
||||
RecipeTimeline(),
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController.addListener(_onScroll);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.removeListener(_onScroll);
|
||||
_freshController.dispose();
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
Future<void> _onRefresh() async {
|
||||
_freshController.finishRefresh();
|
||||
}
|
||||
|
||||
// 上拉加载
|
||||
Future<void> _onLoad() async {
|
||||
final provider = context.read<FoodProvider>();
|
||||
if (provider.hasMore) {
|
||||
await provider.refreshFoodStats();
|
||||
_freshController.finishLoad(IndicatorResult.success);
|
||||
} else {
|
||||
_freshController.finishLoad(IndicatorResult.noMore);
|
||||
}
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
// 当滚动距离超过时显示返回顶部按钮
|
||||
if (_scrollController.offset > 100) {
|
||||
if (!_showScrollToTop) {
|
||||
setState(() {
|
||||
_showScrollToTop = true;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (_showScrollToTop) {
|
||||
setState(() {
|
||||
_showScrollToTop = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动到顶部
|
||||
void _scrollToTop() => scrollToTopAnimateTo(_scrollController);
|
||||
|
||||
Widget _buildTabs({
|
||||
required BuildContext context,
|
||||
required RecordTab currentTab,
|
||||
@@ -52,24 +113,135 @@ class _RecordPageState extends State<RecordPage> {
|
||||
provider.onRecordTabChange();
|
||||
}
|
||||
|
||||
void _handleAddRecord(FoodProvider provider) {
|
||||
provider.resetRecordForm();
|
||||
provider.isEditing = false;
|
||||
Navigator.pop(context);
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
isScrollControlled: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
builder: (context) => RecordForm(),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleAddMoment(FoodProvider provider) {
|
||||
provider.resetMomentForm();
|
||||
provider.isEditing = false;
|
||||
Navigator.pop(context);
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
isScrollControlled: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
builder: (context) => MomentForm(),
|
||||
);
|
||||
}
|
||||
|
||||
void _onPressAdd(FoodProvider provider) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
builder:
|
||||
(context) => Padding(
|
||||
padding: EdgeInsetsGeometry.symmetric(horizontal: 0, vertical: 8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Text('请选择操作', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 10),
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.book,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
title: Text(
|
||||
'新增菜谱',
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
onTap: () => {},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.note_add,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
title: Text(
|
||||
'新增记录',
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
onTap: () => _handleAddRecord(provider),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.group,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
title: Text(
|
||||
'发布朋友圈',
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
onTap: () => _handleAddMoment(provider),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = context.watch<FoodProvider>();
|
||||
|
||||
return Column(
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
title: Text('记录', style: Theme.of(context).textTheme.titleLarge),
|
||||
centerTitle: true,
|
||||
leading: Builder(
|
||||
builder:
|
||||
(context) => IconButton(
|
||||
icon: Icon(Icons.menu, color: Theme.of(context).primaryColor),
|
||||
onPressed: () => {},
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.search, color: Theme.of(context).primaryColor),
|
||||
onPressed: () {},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.add, color: Theme.of(context).primaryColor),
|
||||
onPressed: () => _onPressAdd(provider),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildTabs(
|
||||
context: context,
|
||||
currentTab: provider.currentRecordTab,
|
||||
onTabChanged: (tab) => _onTabChange(tab, provider),
|
||||
),
|
||||
Expanded(
|
||||
child: IndexedStack(
|
||||
index: provider.currentRecordTab.index,
|
||||
children: _tabPages,
|
||||
),
|
||||
),
|
||||
Expanded(child: _tabPages[provider.currentRecordTab.index]),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_common/utils/minio_utils.dart';
|
||||
import 'package:flutter_common/utils/toast_util.dart';
|
||||
import 'package:flutter_common/widget/common_widget.dart';
|
||||
import 'package:flutter_common/widget/dialog_widget.dart';
|
||||
import 'package:flutter_common/widget/loading_widget.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:food_hub_app/apis/recipe.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
import 'package:food_hub_app/provider/food_provider.dart';
|
||||
import 'package:food_hub_app/utils/minio_utils.dart';
|
||||
import 'package:food_hub_app/widgets/common/form.dart';
|
||||
import 'package:food_hub_app/widgets/common/image.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class RecordFormPage extends StatefulWidget {
|
||||
const RecordFormPage({super.key});
|
||||
class RecordForm extends StatefulWidget {
|
||||
const RecordForm({super.key});
|
||||
|
||||
@override
|
||||
State<RecordFormPage> createState() => _RecordFormPageState();
|
||||
State<RecordForm> createState() => _RecordFormState();
|
||||
}
|
||||
|
||||
class _RecordFormPageState extends State<RecordFormPage> {
|
||||
class _RecordFormState extends State<RecordForm> {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
static const String _nameField = 'name';
|
||||
static const String _dateField = 'date';
|
||||
@@ -49,19 +49,19 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildFormLabel(context: context, text: '菜谱名称', isRequired: true),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 12),
|
||||
_buildRecipeField(provider),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
buildFormLabel(context: context, text: '完成时间', isRequired: true),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 12),
|
||||
_buildDateField(provider),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
buildFormLabel(context: context, text: '上传图片', isRequired: true),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 12),
|
||||
_buildImageField(provider),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
buildFormButtonGroup(
|
||||
context: context,
|
||||
@@ -254,13 +254,18 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
||||
PlatformFile file = result.files.first;
|
||||
LoadingDialog.show(context, message: '上传中');
|
||||
|
||||
try {
|
||||
final fileName = await MinIOHelper().uploadFile(
|
||||
bucketName: AppConfig.bucketName,
|
||||
file: file,
|
||||
);
|
||||
|
||||
LoadingDialog.hide(context);
|
||||
ToastUtil.success('上传成功');
|
||||
provider.updateRecordFormItem(imageUrl: fileName);
|
||||
} catch (e) {
|
||||
ToastUtil.error(e.toString());
|
||||
} finally {
|
||||
LoadingDialog.hide(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,24 +331,17 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final provider = context.watch<FoodProvider>();
|
||||
|
||||
final String title = provider.isEditing ? '编辑记录' : '新增记录';
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(title, style: TextStyle(color: Colors.white)),
|
||||
backgroundColor: colors.primary,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: CommonCard(child: _buildFormBuilder(provider)),
|
||||
child: GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(
|
||||
glassColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: _buildFormBuilder(provider),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_common/widget/chart.dart';
|
||||
import 'package:flutter_common/widget/common_widget.dart';
|
||||
import 'package:food_hub_app/provider/food_provider.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class StatsPage extends StatefulWidget {
|
||||
@@ -24,7 +25,9 @@ class _StatsPage extends State<StatsPage> {
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildSummaryStats(FoodProvider provider) {
|
||||
Widget _buildSummaryStats(BuildContext context, FoodProvider provider) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return GridView.count(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
@@ -34,42 +37,62 @@ class _StatsPage extends State<StatsPage> {
|
||||
mainAxisSpacing: 8,
|
||||
childAspectRatio: 2,
|
||||
children: [
|
||||
StatsCard(
|
||||
GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: StatsCard(
|
||||
icon: Icons.restaurant_menu,
|
||||
title: '菜谱总数',
|
||||
value: provider.summaryStats.recipeCount.toString(),
|
||||
unit: '个',
|
||||
),
|
||||
StatsCard(
|
||||
),
|
||||
GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: StatsCard(
|
||||
icon: Icons.grid_view_rounded,
|
||||
title: '菜谱类别',
|
||||
value: provider.summaryStats.categoryCount.toString(),
|
||||
unit: '种',
|
||||
),
|
||||
StatsCard(
|
||||
),
|
||||
GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: StatsCard(
|
||||
icon: Icons.flag,
|
||||
title: '做菜次数',
|
||||
value: provider.summaryStats.workCount.toString(),
|
||||
unit: '个',
|
||||
),
|
||||
StatsCard(
|
||||
),
|
||||
GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: StatsCard(
|
||||
icon: Icons.show_chart,
|
||||
title: '平均次数',
|
||||
value: provider.averageRecordCount.toStringAsFixed(2),
|
||||
unit: '次/月',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(FoodProvider provider) {
|
||||
Widget _buildContent(BuildContext context, FoodProvider provider) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
_buildSummaryStats(provider),
|
||||
_buildSummaryStats(context, provider),
|
||||
SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: chartHeight,
|
||||
child: CommonCard(
|
||||
child: GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: LineChart(
|
||||
title: '记录统计',
|
||||
xAxisName: '日期',
|
||||
@@ -82,7 +105,9 @@ class _StatsPage extends State<StatsPage> {
|
||||
SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: chartHeight,
|
||||
child: CommonCard(
|
||||
child: GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: PieChart(
|
||||
title: '菜谱统计',
|
||||
unit: '个',
|
||||
@@ -93,7 +118,9 @@ class _StatsPage extends State<StatsPage> {
|
||||
SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: chartHeight,
|
||||
child: CommonCard(
|
||||
child: GlassCard(
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: RankChart(title: '排行榜', data: provider.rankStats, unit: '次'),
|
||||
),
|
||||
),
|
||||
@@ -105,13 +132,28 @@ class _StatsPage extends State<StatsPage> {
|
||||
Widget build(BuildContext context) {
|
||||
final provider = context.watch<FoodProvider>();
|
||||
|
||||
return Stack(
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
title: Text('统计', style: Theme.of(context).textTheme.titleLarge),
|
||||
centerTitle: true,
|
||||
automaticallyImplyLeading: false,
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsetsGeometry.all(10),
|
||||
child: Stack(
|
||||
children: [
|
||||
if (provider.isLoading)
|
||||
buildLoadingIndicator()
|
||||
else
|
||||
SingleChildScrollView(child: _buildContent(provider)),
|
||||
SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 90),
|
||||
child: _buildContent(context, provider),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,15 +43,17 @@ Widget buildScrollToTop({
|
||||
required BuildContext context,
|
||||
required VoidCallback scrollToTop,
|
||||
}) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return Positioned(
|
||||
right: 0,
|
||||
bottom: 20,
|
||||
bottom: 80,
|
||||
child: FloatingActionButton(
|
||||
onPressed: scrollToTop,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
backgroundColor: colors.primary,
|
||||
elevation: 2,
|
||||
mini: true,
|
||||
child: const Icon(Icons.arrow_upward, color: Colors.white),
|
||||
child: Icon(Icons.arrow_upward, color: colors.surface),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,26 +12,26 @@ InputDecoration buildInputDecoration({
|
||||
|
||||
return InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(color: colors.onSurface.withAlpha(100)),
|
||||
hintStyle: Theme.of(context).textTheme.bodyMedium,
|
||||
filled: true,
|
||||
fillColor: colors.surface,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: Color(0xFFE5E7EB)),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: colors.primary, width: 2),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: Colors.red, width: 2),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 14),
|
||||
errorStyle: const TextStyle(fontSize: 12, height: 1, color: Colors.red),
|
||||
prefixIcon:
|
||||
prefixIcon != null
|
||||
? Icon(prefixIcon, size: 20, color: Color(0xFF86909C))
|
||||
? Icon(prefixIcon, size: 20, color: colors.primary)
|
||||
: null,
|
||||
prefixIconConstraints: const BoxConstraints(minWidth: 40),
|
||||
isDense: true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
@@ -53,6 +54,8 @@ class _ImagePreviewPageState extends State<ImagePreviewPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final index = '${_currentIndex + 1}/${widget.images.length}';
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: AppBar(
|
||||
@@ -63,10 +66,7 @@ class _ImagePreviewPageState extends State<ImagePreviewPage> {
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
// 显示实时更新的页码(当前索引+1 / 总数量)
|
||||
title: Text(
|
||||
'${_currentIndex + 1}/${widget.images.length}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
title: Text(index, style: const TextStyle(color: Colors.white)),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: PhotoViewGallery(
|
||||
@@ -95,21 +95,6 @@ class CommonImage extends StatelessWidget {
|
||||
|
||||
const CommonImage({super.key, required this.imageUrls, required this.index});
|
||||
|
||||
Widget _buildImageLoadingIndicator(ImageChunkEvent? progress) {
|
||||
final value =
|
||||
progress?.expectedTotalBytes != null
|
||||
? progress!.cumulativeBytesLoaded / progress.expectedTotalBytes!
|
||||
: null;
|
||||
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: CircularProgressIndicator(value: value),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorImage() {
|
||||
return Container(
|
||||
color: Colors.grey[200],
|
||||
@@ -140,14 +125,16 @@ class CommonImage extends StatelessWidget {
|
||||
onTap: () {
|
||||
_navigateToImagePreview(context, imageUrls, index);
|
||||
},
|
||||
child: Image.network(
|
||||
'${AppConfig.imageBaseUrl}${imageUrls[index]}',
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: '${AppConfig.imageBaseUrl}${imageUrls[index]}',
|
||||
fit: BoxFit.cover,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return _buildImageLoadingIndicator(loadingProgress);
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) => _buildErrorImage(),
|
||||
progressIndicatorBuilder:
|
||||
(context, url, downloadProgress) => CircularProgressIndicator(
|
||||
strokeWidth: 1.5,
|
||||
padding: EdgeInsetsGeometry.all(20),
|
||||
value: downloadProgress.progress,
|
||||
),
|
||||
errorWidget: (context, error, stackTrace) => _buildErrorImage(),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -213,19 +200,19 @@ Widget buildImageUploadButton({
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
width: 96,
|
||||
height: 96,
|
||||
width: double.infinity,
|
||||
height: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: colors.surface,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.black),
|
||||
border: Border.all(color: colors.surface),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add, color: colors.onSurface.withAlpha(100)),
|
||||
Icon(Icons.camera_alt, color: colors.onSurface.withAlpha(100)),
|
||||
SizedBox(height: 6),
|
||||
Text('添加图片', style: TextStyle(color: colors.onSurface.withAlpha(100))),
|
||||
Text('点击上传美食图片', style: Theme.of(context).textTheme.bodyMedium),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -43,8 +43,8 @@ Widget buildToggleSwitch<T extends Enum>({
|
||||
totalSwitches: tabValues.length,
|
||||
labels: labels,
|
||||
activeBgColor: [colors.primary],
|
||||
activeFgColor: Colors.white,
|
||||
inactiveBgColor: colors.surfaceContainer,
|
||||
activeFgColor: colors.surface,
|
||||
inactiveBgColor: colors.surface,
|
||||
inactiveFgColor: colors.onSurface,
|
||||
cornerRadius: 12.0,
|
||||
customTextStyles: [TextStyle(fontSize: 12, fontWeight: FontWeight.w500)],
|
||||
@@ -85,10 +85,10 @@ Widget buildTag(BuildContext context, String title) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: colors.primary.withAlpha(60),
|
||||
color: colors.primary,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(title, style: TextStyle(color: colors.primary)),
|
||||
child: Text(title, style: TextStyle(color: colors.surface)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -135,24 +135,10 @@ Widget buildInfoButton({
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
elevation: 0,
|
||||
),
|
||||
child: Text(text, style: TextStyle(color: Colors.white)),
|
||||
child: Text(text),
|
||||
);
|
||||
}
|
||||
|
||||
/// 取消按钮样式
|
||||
ButtonStyle cancelButtonStyle() {
|
||||
return ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Colors.grey),
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Text buttonText({required String text}) {
|
||||
return Text(text, style: TextStyle(color: Colors.white));
|
||||
}
|
||||
|
||||
/// 图片错误显示
|
||||
Widget errorImageContainer(double height) {
|
||||
return Container(
|
||||
|
||||
@@ -7,7 +7,6 @@ import 'package:food_hub_app/provider/food_provider.dart';
|
||||
import 'package:food_hub_app/provider/user_provider.dart';
|
||||
import 'package:food_hub_app/widgets/common/image.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||
|
||||
class MomentCard extends StatefulWidget {
|
||||
final Moment moment;
|
||||
@@ -26,21 +25,24 @@ class MomentCardState extends State<MomentCard> {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InkWell(onTap: () => _onTapUser(context), child: _buildAvatar()),
|
||||
InkWell(
|
||||
onTap: () => _onTapUser(context),
|
||||
child: _buildAvatar(context),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildNickname(),
|
||||
_buildNickname(context),
|
||||
const SizedBox(height: 5),
|
||||
_buildContent(),
|
||||
_buildContent(context),
|
||||
if (widget.moment.imageList.isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
_buildPostImages(),
|
||||
],
|
||||
const SizedBox(height: 10),
|
||||
_buildTime(),
|
||||
_buildTime(context),
|
||||
const SizedBox(height: 5),
|
||||
_buildActionButtons(),
|
||||
if (widget.moment.commentList!.isNotEmpty) ...[
|
||||
@@ -69,38 +71,35 @@ class MomentCardState extends State<MomentCard> {
|
||||
}
|
||||
|
||||
// 构建头像组件
|
||||
Widget _buildAvatar() {
|
||||
Widget _buildAvatar(BuildContext context) {
|
||||
if (widget.moment.avatar!.isEmpty) {
|
||||
return TDAvatar(
|
||||
size: TDAvatarSize.medium,
|
||||
type: TDAvatarType.customText,
|
||||
shape: TDAvatarShape.circle,
|
||||
return CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
text: widget.moment.username?[0],
|
||||
foregroundColor: Theme.of(context).colorScheme.surface,
|
||||
child: Text(widget.moment.username![0], style: TextStyle(fontSize: 14)),
|
||||
);
|
||||
} else {
|
||||
return TDAvatar(
|
||||
size: TDAvatarSize.medium,
|
||||
type: TDAvatarType.normal,
|
||||
fit: BoxFit.contain,
|
||||
avatarUrl: '${AppConfig.imageBaseUrl}/${widget.moment.avatar}',
|
||||
return CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundImage: NetworkImage('${AppConfig.imageBaseUrl}/${widget.moment.avatar}')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建昵称组件
|
||||
Widget _buildNickname() {
|
||||
Widget _buildNickname(BuildContext context) {
|
||||
return Text(
|
||||
widget.moment.username ?? "",
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
);
|
||||
}
|
||||
|
||||
// 构建内容组件
|
||||
Widget _buildContent() {
|
||||
Widget _buildContent(BuildContext context) {
|
||||
return Text(
|
||||
widget.moment.content,
|
||||
style: const TextStyle(fontSize: 15, height: 1.3),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,10 +141,10 @@ class MomentCardState extends State<MomentCard> {
|
||||
}
|
||||
|
||||
// 构建时间组件
|
||||
Widget _buildTime() {
|
||||
Widget _buildTime(BuildContext context) {
|
||||
return Text(
|
||||
widget.moment.date ?? "",
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -178,14 +177,14 @@ class MomentCardState extends State<MomentCard> {
|
||||
alignment: Alignment.bottomLeft,
|
||||
children: [
|
||||
Icon(Icons.thumb_up_alt_outlined),
|
||||
Positioned(
|
||||
left: 20,
|
||||
bottom: 15,
|
||||
child: TDBadge(
|
||||
TDBadgeType.message,
|
||||
count: (widget.moment.likeList?.length ?? 0).toString(),
|
||||
),
|
||||
),
|
||||
// Positioned(
|
||||
// left: 20,
|
||||
// bottom: 15,
|
||||
// child: TDBadge(
|
||||
// TDBadgeType.message,
|
||||
// count: (widget.moment.likeList?.length ?? 0).toString(),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -196,14 +195,14 @@ class MomentCardState extends State<MomentCard> {
|
||||
alignment: Alignment.bottomLeft,
|
||||
children: [
|
||||
Icon(Icons.comment_outlined),
|
||||
Positioned(
|
||||
left: 20,
|
||||
bottom: 15,
|
||||
child: TDBadge(
|
||||
TDBadgeType.message,
|
||||
count: (widget.moment.commentList?.length ?? 0).toString(),
|
||||
),
|
||||
),
|
||||
// Positioned(
|
||||
// left: 20,
|
||||
// bottom: 15,
|
||||
// child: TDBadge(
|
||||
// TDBadgeType.message,
|
||||
// count: (widget.moment.commentList?.length ?? 0).toString(),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -4,7 +4,8 @@ import 'package:flutter_common/widget/common_widget.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
import 'package:food_hub_app/models/session.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:liquid_glass_widgets/widgets/containers/glass_card.dart';
|
||||
|
||||
class ProfileInfo extends StatelessWidget {
|
||||
final User user;
|
||||
@@ -13,19 +14,21 @@ class ProfileInfo extends StatelessWidget {
|
||||
|
||||
Widget _buildAvatar(BuildContext context, User user) {
|
||||
if (user.avatar == null) {
|
||||
return TDAvatar(
|
||||
size: TDAvatarSize.large,
|
||||
type: TDAvatarType.customText,
|
||||
shape: TDAvatarShape.circle,
|
||||
return CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
text: user.username.isNotEmpty ? user.username[0] : '?',
|
||||
foregroundColor: Theme.of(context).colorScheme.surface,
|
||||
child: Text(
|
||||
user.username.isNotEmpty ? user.username[0] : '?',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return TDAvatar(
|
||||
size: TDAvatarSize.large,
|
||||
type: TDAvatarType.normal,
|
||||
fit: BoxFit.contain,
|
||||
avatarUrl: '${AppConfig.imageBaseUrl}/${user.avatar}',
|
||||
return CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundImage: NetworkImage(
|
||||
'${AppConfig.imageBaseUrl}/${user.avatar}',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -41,22 +44,24 @@ class ProfileInfo extends StatelessWidget {
|
||||
children: [
|
||||
Icon(icon, size: 16, color: colors.primary),
|
||||
const SizedBox(width: 6),
|
||||
Text(text),
|
||||
Text(text, style: Theme.of(context).textTheme.bodyMedium),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonCard(
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return GlassCard(
|
||||
padding: EdgeInsetsGeometry.all(10),
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildAvatar(context, user),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
user.username,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(user.username, style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
if (user.tags != null && user.tags!.isNotEmpty)
|
||||
Row(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_common/widget/common_widget.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/provider/food_provider.dart';
|
||||
import 'package:food_hub_app/utils/index.dart';
|
||||
import 'package:food_hub_app/views/record_form.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -62,19 +63,16 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||
Text(formatDateTime(provider.selectedDay, 'MM月dd日 EEEE')),
|
||||
SizedBox(height: 4),
|
||||
if (provider.selectRecordList.isEmpty)
|
||||
buildEmptyData()
|
||||
SizedBox(height: 100, child: buildEmptyData())
|
||||
else
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
itemCount: provider.selectRecordList.length,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8),
|
||||
itemBuilder: (context, index) {
|
||||
return _buildRecordItem(
|
||||
context,
|
||||
provider.selectRecordList[index],
|
||||
Column(
|
||||
children:
|
||||
provider.selectRecordList.map((record) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: _buildRecordItem(context, record),
|
||||
);
|
||||
},
|
||||
),
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -112,7 +110,15 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||
|
||||
provider.initRecordForm(record);
|
||||
provider.isEditing = true;
|
||||
Navigator.pushNamed(context, "/recordForm");
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
builder: (context) => RecordForm(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRecordItem(BuildContext context, FoodRecord record) {
|
||||
@@ -195,12 +201,15 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||
}
|
||||
|
||||
Widget _buildContent(FoodProvider provider) {
|
||||
return Column(
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 80),
|
||||
child: Column(
|
||||
children: [
|
||||
CommonCard(child: _recipeCalendar(provider)),
|
||||
SizedBox(height: 8),
|
||||
Expanded(child: CommonCard(child: _dailyItem(context, provider))),
|
||||
const SizedBox(height: 8),
|
||||
CommonCard(child: _dailyItem(context, provider)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -208,13 +217,10 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
|
||||
Widget build(BuildContext context) {
|
||||
final provider = context.watch<FoodProvider>();
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
if (provider.isLoading)
|
||||
buildLoadingIndicator()
|
||||
else
|
||||
_buildContent(provider),
|
||||
],
|
||||
);
|
||||
if (provider.isLoading) {
|
||||
return buildLoadingIndicator();
|
||||
}
|
||||
|
||||
return _buildContent(provider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_common/widget/common_widget.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/widgets/common/image.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
|
||||
class RecipeCard extends StatelessWidget {
|
||||
final RecipeSummary recipe;
|
||||
@@ -17,14 +18,22 @@ class RecipeCard extends StatelessWidget {
|
||||
final List<String> imageUrls =
|
||||
recipe.recordList.reversed.map((record) => record.imageUrl).toList();
|
||||
|
||||
return CommonCard(
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return GlassCard(
|
||||
padding: EdgeInsetsGeometry.all(10),
|
||||
useOwnLayer: true,
|
||||
settings: LiquidGlassSettings(glassColor: colors.surface),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CommonImage(imageUrls: imageUrls, index: 0),
|
||||
SizedBox(height: 8),
|
||||
_buildRecipeContent(context),
|
||||
InkWell(
|
||||
onTap: () => navigatorToRecipeDetail(context),
|
||||
child: _buildRecipeContent(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -32,6 +41,7 @@ class RecipeCard extends StatelessWidget {
|
||||
|
||||
Widget _buildRecipeContent(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -42,17 +52,10 @@ class RecipeCard extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
recipe.name,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
style: textTheme.titleMedium,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => navigatorToRecipeDetail(context),
|
||||
child: Icon(
|
||||
Icons.arrow_circle_right_sharp,
|
||||
color: colors.secondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
@@ -62,19 +65,13 @@ class RecipeCard extends StatelessWidget {
|
||||
_buildIconText(
|
||||
icon: Icons.food_bank,
|
||||
text: recipe.category,
|
||||
color: Color(0xFF6A5ACD),
|
||||
color: colors.primary,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_buildIconText(
|
||||
icon: Icons.thumb_up_alt_outlined,
|
||||
text: recipe.likeCount.toString(),
|
||||
color: Color(0xFFDC143C),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_buildIconText(
|
||||
icon: Icons.star_outline,
|
||||
text: recipe.favouriteCount.toString(),
|
||||
color: Color(0xFF20B2AA),
|
||||
icon: Icons.note_add,
|
||||
text: recipe.recordList.length.toString(),
|
||||
color: colors.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -23,55 +23,9 @@ class _RecipeListState extends State<RecipeList> {
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildSelectCategory(FoodProvider provider) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
width: 140,
|
||||
decoration: BoxDecoration(
|
||||
color: colors.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: DropdownButton<String>(
|
||||
value: provider.queryCategory,
|
||||
isExpanded: true,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
dropdownColor: colors.surfaceContainer,
|
||||
elevation: 6,
|
||||
underline: Container(),
|
||||
items:
|
||||
provider.categoryList.map((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: colors.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(value, style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
provider.queryCategory = value!;
|
||||
provider.refreshRecipeList();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRecipeList(FoodProvider provider) {
|
||||
return GridView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 80),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 8,
|
||||
@@ -91,8 +45,6 @@ class _RecipeListState extends State<RecipeList> {
|
||||
} else {
|
||||
return Column(
|
||||
children: [
|
||||
_buildSelectCategory(provider),
|
||||
SizedBox(height: 5),
|
||||
Expanded(child: _buildRecipeList(provider)),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -28,26 +28,24 @@ class _RecipeTimeline extends State<RecipeTimeline> {
|
||||
SizedBox(width: 5),
|
||||
Text(
|
||||
record.date,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 6),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 0),
|
||||
child: CommonCard(
|
||||
SizedBox(height: 8),
|
||||
CommonCard(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
record.name,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
CommonImage(imageUrls: [record.imageUrl], index: 0),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20)
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -59,6 +57,7 @@ class _RecipeTimeline extends State<RecipeTimeline> {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return Timeline.tileBuilder(
|
||||
padding: const EdgeInsets.only(bottom: 80),
|
||||
theme: TimelineThemeData(nodePosition: 0, indicatorPosition: 0),
|
||||
builder: TimelineTileBuilder.connected(
|
||||
itemCount: recordList.length,
|
||||
|
||||
@@ -6,14 +6,10 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <rive_native/rive_native_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) rive_native_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "RiveNativePlugin");
|
||||
rive_native_plugin_register_with_registrar(rive_native_registrar);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_linux
|
||||
rive_native
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
@@ -6,19 +6,19 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import file_picker
|
||||
import file_selector_macos
|
||||
import flutter_image_compress_macos
|
||||
import path_provider_foundation
|
||||
import rive_native
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
import sqflite_darwin
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
RiveNativePlugin.register(with: registry.registrar(forPlugin: "RiveNativePlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
}
|
||||
|
||||
220
pubspec.lock
220
pubspec.lock
@@ -121,6 +121,30 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "8.10.1"
|
||||
cached_network_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cached_network_image
|
||||
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
cached_network_image_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_platform_interface
|
||||
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.1.1"
|
||||
cached_network_image_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_web
|
||||
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -213,18 +237,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio
|
||||
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
|
||||
sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.8.0+1"
|
||||
version: "5.9.2"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
||||
sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
easy_refresh:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -233,14 +257,22 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
equatable:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -310,6 +342,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
flutter_carousel_widget:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -402,22 +442,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.28"
|
||||
flutter_slidable:
|
||||
flutter_shaders:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_slidable
|
||||
sha256: a857de7ea701f276fd6a6c4c67ae885b60729a3449e42766bb0e655171042801
|
||||
name: flutter_shaders
|
||||
sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
flutter_swiper_null_safety:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_swiper_null_safety
|
||||
sha256: "5a855e0080d035c08e82f8b7fd2f106344943a30c9ab483b2584860a2f22eaaf"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "0.1.3"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -432,10 +464,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fluttertoast
|
||||
sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1"
|
||||
sha256: "90778fe0497fe3a09166e8cf2e0867310ff434b794526589e77ec03cf08ba8e8"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "8.2.12"
|
||||
version: "8.2.14"
|
||||
form_builder_validators:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -560,10 +592,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
version: "0.20.2"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -600,26 +632,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "10.0.8"
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -628,6 +660,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
liquid_glass_widgets:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: liquid_glass_widgets
|
||||
sha256: "776adcdb7d48af0b935642425936813074355830eb0a47ecca8b227549d5b057"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.16.3"
|
||||
logger:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -664,10 +704,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -692,6 +732,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: octo_image
|
||||
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -840,18 +888,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rive
|
||||
sha256: fc0abf65d03d1c9afaeb35be9e71c7cf04d2d1f76e94e69d2af1b3ba413cddf9
|
||||
sha256: "84a640f48122679e48397935ed1b971db19ef2e44bfcbae60e42fb5ce29a0a4e"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.14.0-dev.14"
|
||||
version: "0.14.9"
|
||||
rive_native:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rive_native
|
||||
sha256: e9c7d36f19eb6d32f563825d4e9d5032b19a36d3ca3341641431035bca022d19
|
||||
sha256: "85dc8413398f7540eab9b73c6d704ecd5051ab9c57e539eed85ccd2a69f119aa"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.17"
|
||||
version: "0.1.9"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rxdart
|
||||
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.28.0"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -872,26 +928,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
version: "2.5.5"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
|
||||
sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.10"
|
||||
version: "2.4.23"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -904,10 +960,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "2.4.2"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -977,6 +1033,46 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: "564cfed0746fe53140c23b70b308e045c3b31f17778f2f326ccb7d804ea0250a"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.2+1"
|
||||
sqflite_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_android
|
||||
sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.2+3"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "1581ffbf7a0e333b380d6a30737d78516b826cb35beb7fb0bf8a3ea0c678b465"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.5.8"
|
||||
sqflite_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_darwin
|
||||
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
sqflite_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_platform_interface
|
||||
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1013,34 +1109,34 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: syncfusion_flutter_charts
|
||||
sha256: c58ca79e072680af6f0554f7c4b91886c1d8808f2522b49d557f16fb5cb3bb04
|
||||
sha256: "68fdb029dad34a46e4c9cfad8ad66fe29db7b303bd96849261ab2b23a168d0e8"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "30.1.41"
|
||||
version: "30.2.7"
|
||||
syncfusion_flutter_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: syncfusion_flutter_core
|
||||
sha256: "536753489e168f49659261d0abd02b101b34c0b1bde27898f2869aab05f1cbb6"
|
||||
sha256: bfd026c0f9822b49ff26fed11cd3334519acb6a6ad4b0c81d9cd18df6af1c4c0
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "30.1.41"
|
||||
version: "30.2.7"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
table_calendar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: table_calendar
|
||||
sha256: b2896b7c86adf3a4d9c911d860120fe3dbe03c85db43b22fd61f14ee78cdbb63
|
||||
sha256: "0c0c6219878b363a2d5f40c7afb159d845f253d061dc3c822aa0d5fe0f721982"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
tdesign_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: tdesign_flutter
|
||||
sha256: b36b6f939f7a585184665202b6b3acf1922728962312b65182dbbff64c3b62f2
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.3"
|
||||
version: "3.2.0"
|
||||
tdesign_flutter_adaptation:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
@@ -1061,10 +1157,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
version: "0.7.7"
|
||||
timelines_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1141,10 +1237,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1218,5 +1314,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.7.0 <4.0.0"
|
||||
flutter: ">=3.29.0"
|
||||
dart: ">=3.10.0 <4.0.0"
|
||||
flutter: ">=3.38.0"
|
||||
|
||||
@@ -1 +1 @@
|
||||
name: food_hub_app
|
||||
name: food_hub_app
|
||||
@@ -6,14 +6,11 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <rive_native/rive_native_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FileSelectorWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
RiveNativePluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("RiveNativePlugin"));
|
||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_windows
|
||||
rive_native
|
||||
share_plus
|
||||
url_launcher_windows
|
||||
|
||||
Reference in New Issue
Block a user