feat: 完成智能电网电工端 Flutter 应用初始化
创建完整的 Flutter 项目结构,包含 Android 和 iOS 平台配置、核心网络层、路由、状态管理及业务模块(订单、收益、认证),适配原 uni-app 项目功能逻辑
This commit is contained in:
commit
56830e94ec
108 files changed
+9272
No files matched your search
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../core/app_keys.dart';
|
||||
import '../core/theme/app_theme.dart';
|
||||
import '../core/theme/app_typography.dart';
|
||||
import 'router.dart';
|
||||
|
||||
class VoltLinkApp extends ConsumerWidget {
|
||||
const VoltLinkApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final router = ref.watch(routerProvider);
|
||||
|
||||
return MaterialApp.router(
|
||||
title: 'VoltLink',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: AppTheme.build(),
|
||||
routerConfig: router,
|
||||
scaffoldMessengerKey: AppKeys.scaffoldMessenger,
|
||||
locale: const Locale('zh', 'CN'),
|
||||
supportedLocales: const [Locale('zh', 'CN'), Locale('en', 'US')],
|
||||
localizationsDelegates: const [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
// 对应源 `page` 上的全局字体设置。
|
||||
builder: (context, child) => DefaultTextStyle(
|
||||
style: appDefaultTextStyle,
|
||||
child: child ?? const SizedBox.shrink(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../core/app_keys.dart';
|
||||
import '../features/auth/application/session_controller.dart';
|
||||
import '../features/auth/presentation/login_page.dart';
|
||||
import '../features/auth/presentation/register_page.dart';
|
||||
import '../features/notice/presentation/notice_center_page.dart';
|
||||
import '../features/order/presentation/order_detail_page.dart';
|
||||
import 'tabs_page.dart';
|
||||
|
||||
/// 会话语义变化时驱动 go_router 重新执行 `redirect`。
|
||||
class _SessionRefresh extends ChangeNotifier {
|
||||
void bump() => notifyListeners();
|
||||
}
|
||||
|
||||
/// 路由表,对应源 `pages.json` 的 5 个页面。
|
||||
///
|
||||
/// 除登录/注册外,其余页面统一使用主题里的 `CupertinoPageTransitionsBuilder`
|
||||
/// (新页面从右侧滑入),与源 `PageTransition.vue` 的表现一致。
|
||||
final routerProvider = Provider<GoRouter>((ref) {
|
||||
final refresh = _SessionRefresh();
|
||||
ref.listen(sessionProvider, (_, _) => refresh.bump());
|
||||
ref.onDispose(refresh.dispose);
|
||||
|
||||
return GoRouter(
|
||||
navigatorKey: AppKeys.rootNavigator,
|
||||
initialLocation: '/login',
|
||||
refreshListenable: refresh,
|
||||
redirect: (context, state) {
|
||||
final loggedIn = ref.read(sessionProvider).isLoggedIn;
|
||||
final atAuth =
|
||||
state.matchedLocation == '/login' || state.matchedLocation == '/register';
|
||||
if (!loggedIn) return atAuth ? null : '/login';
|
||||
if (atAuth) return '/tabs';
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
GoRoute(path: '/login', builder: (context, state) => const LoginPage()),
|
||||
GoRoute(
|
||||
path: '/register',
|
||||
builder: (context, state) => const RegisterPage(),
|
||||
),
|
||||
GoRoute(path: '/tabs', builder: (context, state) => const TabsPage()),
|
||||
|
||||
// 消息中心:顶栏铃铛入口
|
||||
GoRoute(
|
||||
path: '/notice',
|
||||
builder: (context, state) => const NoticeCenterPage(),
|
||||
),
|
||||
|
||||
// 订单详情:source 为 repair|install|maintenance|survey
|
||||
GoRoute(
|
||||
path: '/order/:source/:id',
|
||||
builder: (context, state) => OrderDetailPage(
|
||||
source: state.pathParameters['source']!,
|
||||
id: int.parse(state.pathParameters['id']!),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
/// 底部导航的三个 Tab,对应 `components/CustomTabBar.vue` 的 3 个入口。
|
||||
enum AppTab { order, earnings, profile }
|
||||
|
||||
extension AppTabKey on AppTab {
|
||||
/// 与 `XBottomNav.items` 中的 key 一致。
|
||||
String get key => switch (this) {
|
||||
AppTab.order => 'order',
|
||||
AppTab.earnings => 'earnings',
|
||||
AppTab.profile => 'profile',
|
||||
};
|
||||
}
|
||||
|
||||
/// 当前 Tab。源里每个页面各自挂 `CustomTabBar` 并用 `redirectTo` 切换,
|
||||
/// Flutter 端改为单容器 + [IndexedStack],避免页面重建、保留各自滚动位置。
|
||||
class TabIndexController extends Notifier<AppTab> {
|
||||
@override
|
||||
AppTab build() => AppTab.order;
|
||||
|
||||
void select(AppTab tab) => state = tab;
|
||||
|
||||
void selectByKey(String key) {
|
||||
state = AppTab.values.firstWhere((t) => t.key == key, orElse: () => state);
|
||||
}
|
||||
}
|
||||
|
||||
final tabIndexProvider = NotifierProvider<TabIndexController, AppTab>(
|
||||
TabIndexController.new,
|
||||
);
|
||||
@@ -0,0 +1,44 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../core/theme/app_colors.dart';
|
||||
import '../features/earnings/presentation/earnings_tab.dart';
|
||||
import '../features/order/presentation/order_tab.dart';
|
||||
import '../features/profile/presentation/profile_tab.dart';
|
||||
import '../shared/widgets/x_bottom_nav.dart';
|
||||
import 'tab_index.dart';
|
||||
|
||||
/// 3 个 Tab 的常驻容器。源项目每个页面各自挂 `CustomTabBar`,
|
||||
/// 这里统一收口到容器层,用 [IndexedStack] 保留各页内部状态。
|
||||
class TabsPage extends ConsumerWidget {
|
||||
const TabsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final current = ref.watch(tabIndexProvider);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.bgPage,
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: IndexedStack(
|
||||
index: current.index,
|
||||
children: const [OrderTab(), EarningsTab(), ProfileTab()],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: XBottomNav(
|
||||
current: current.key,
|
||||
onChanged: (key) =>
|
||||
ref.read(tabIndexProvider.notifier).selectByKey(key),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user