ar_tourism_flutter_unity/lib/main.dart
2025-05-14 17:04:13 +08:00

518 lines
17 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'pages/tabs/tabs.dart';
import 'providers/auth_provider.dart';
import 'router/index.dart';
import 'services/online_time_service.dart';
import 'services/update_dialog_service.dart';
import 'components/WelcomeDialog.dart';
import 'package:flutter/services.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
// 自定义页面转场禁用Android手势返回
class NoTransitionPageTransitionsBuilder extends PageTransitionsBuilder {
const NoTransitionPageTransitionsBuilder();
@override
Widget buildTransitions<T>(
PageRoute<T> route,
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return child;
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 在APP启动时完全重置版本检查状态
UpdateDialogService().resetAllCheckStatus();
try {
final packageInfo = await PackageInfo.fromPlatform();
print('应用启动版本: ${packageInfo.version}');
} catch (e) {
print('启动时获取版本信息失败: $e');
}
// 获取登录状态
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
final userId = prefs.getString('userId');
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AuthProvider()..setToken(token, userId: userId)),
],
child: const MyApp(),
),
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
final OnlineTimeService _onlineTimeService = OnlineTimeService();
bool _showedWelcomeDialog = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_onlineTimeService.initWebSocket();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_onlineTimeService.dispose();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
_onlineTimeService.initWebSocket();
break;
case AppLifecycleState.paused:
_onlineTimeService.dispose();
break;
default:
break;
}
}
// 显示欢迎弹窗
void _showWelcomeDialog(BuildContext context) {
// 如果已显示过或用户已登录,则不再显示
if (_showedWelcomeDialog) return;
_showedWelcomeDialog = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
showGeneralDialog(
context: context,
barrierColor: Colors.black.withOpacity(0.5),
barrierDismissible: false,
pageBuilder: (_, __, ___) {
return WelcomeDialog(
onAgree: () {
Navigator.of(context).pop();
Navigator.pushReplacementNamed(context, '/login');
},
onBasicMode: () {
// 先关闭弹窗
Navigator.of(context).pop();
// 直接使用全局导航器进行页面导航
navigatorKey.currentState?.pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => const StartPageOne(),
),
(route) => false,
);
},
onDisagree: () {
// 退出应用
SystemNavigator.pop();
},
);
},
);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
title: '看那',
theme: ThemeData(
useMaterial3: false, //Tabbar取消下划线
primaryColor: const Color.fromRGBO(255, 255, 255, 0.3),
appBarTheme: AppBarTheme(
centerTitle: true,
backgroundColor: const Color(0xff0C0C16),
titleTextStyle: Typography.dense2014.bodyLarge,
),
// 禁用Android手势返回
pageTransitionsTheme: PageTransitionsTheme(
builders: {
TargetPlatform.android: NoTransitionPageTransitionsBuilder(),
TargetPlatform.iOS: NoTransitionPageTransitionsBuilder(),
},
),
platform: TargetPlatform.android,
// 定义默认的输入装饰主题
inputDecorationTheme: const InputDecorationTheme(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color.fromRGBO(255, 255, 255, 0.3)),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color.fromRGBO(255, 255, 255, 0.3)),
),
labelStyle: TextStyle(color: Color.fromRGBO(255, 255, 255, 0.3)),
hintStyle: TextStyle(color: Color.fromRGBO(255, 255, 255, 0.3)),
),
// 定义默认的文本选择主题
textSelectionTheme: TextSelectionThemeData(
cursorColor: const Color.fromRGBO(255, 255, 255, 0.3), // 光标颜色
selectionColor:
const Color.fromRGBO(255, 255, 255, 0.3).withOpacity(0.4), // 选择颜色
selectionHandleColor:
const Color.fromRGBO(255, 255, 255, 0.3), // 选择句柄颜色
),
),
home: Builder(
builder: (context) {
// 只有在未登录时才显示欢迎弹窗
if (!Provider.of<AuthProvider>(context).isLoggedIn) {
_showWelcomeDialog(context);
}
return Provider.of<AuthProvider>(context).isLoggedIn
? const Tabs(initialIndex: 0)
: const SplashScreen();
},
),
// Initialize your routes and handle routing
// initialRoute: "/", 没有home的时候直接进入tabs
onGenerateRoute: onGenerateRoute,
);
}
}
// SplashScreen Widget - 修改后不会自动跳转
class SplashScreen extends StatelessWidget {
const SplashScreen({super.key});
@override
Widget build(BuildContext context) {
// 移除自动跳转逻辑
return GestureDetector(
// 添加点击事件,允许用户通过点击跳转到下一页
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const StartPageOne()),
);
},
child: Container(
width: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('images/start.png'),
fit: BoxFit.fill,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 100),
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: const Image(
image: AssetImage('images/logo.png'),
width: 36,
height: 36,
fit: BoxFit.fill,
),
),
const SizedBox(height: 20),
const Text(
'看那,探索MR元宇宙',
style: TextStyle(
fontSize: 16,
color: Colors.white,
decoration: TextDecoration.none),
),
],
),
),
);
}
}
// StartPageOne Widget
class StartPageOne extends StatefulWidget {
const StartPageOne({super.key});
@override
_StartPageOneState createState() => _StartPageOneState();
}
class _StartPageOneState extends State<StartPageOne> {
int _currentIndex = 0;
late PageController _pageController;
@override
void initState() {
super.initState();
_pageController = PageController();
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xff0C0C16),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(5), // 设置圆角半径
child: const Image(
image: AssetImage('images/logo.png'),
width: 18,
height: 18,
fit: BoxFit.fill,
),
),
const SizedBox(width: 10),
const Text(
'看那',
style: TextStyle(fontSize: 18, color: Colors.white),
),
],
),
const SizedBox(
height: 30,
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.5,
child: PageView(
controller: _pageController,
scrollDirection: Axis.horizontal,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.25,
margin: const EdgeInsets.only(bottom: 10),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
'images/loading1.png'), // Replace with your start.png path
fit: BoxFit.fill,
),
),
),
const Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'奇幻MR之旅',
style: TextStyle(
fontSize: 32,
color: Colors.white,
fontWeight: FontWeight.bold),
),
Image(
image: AssetImage('images/trip_new.png'),
width: 18,
height: 18,
fit: BoxFit.fill,
),
],
),
const SizedBox(height: 10),
const Text(
'享受到一种全新、沉浸式的MR旅游体验',
style: TextStyle(fontSize: 12, color: Color(0xff4E515A)),
),
const SizedBox(height: 10),
const Image(
image: AssetImage('images/decration.png'),
width: 60,
height: 20,
fit: BoxFit.fill,
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.25,
margin: const EdgeInsets.only(bottom: 10),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('images/loading2.png'),
fit: BoxFit.fill,
),
),
),
const Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'探秘MR艺术展',
style: TextStyle(
fontSize: 32,
color: Colors.white,
fontWeight: FontWeight.bold),
),
Image(
image: AssetImage('images/trip_new.png'),
width: 20,
height: 20,
fit: BoxFit.fill,
),
],
),
const SizedBox(height: 10),
const Text(
'领略一场震撼、交互式的MR视觉盛宴',
style: TextStyle(fontSize: 12, color: Color(0xff4E515A)),
),
const SizedBox(height: 10),
const Image(
image: AssetImage('images/decration.png'),
width: 60,
height: 20,
fit: BoxFit.fill,
),
],
),
],
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(2, (index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 12,
height: 12,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _currentIndex == index ? Colors.white : Colors.grey,
),
);
}),
),
const SizedBox(height: 32),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: const LinearGradient(colors: [
Color(0xff80DAA4),
Color(0xff79DDED),
]),
),
child: ElevatedButton(
onPressed: () {
if (_currentIndex == 0) {
setState(() {
_currentIndex = 1;
});
_pageController.animateToPage(
_currentIndex,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
} else {
// 跳转主页
Navigator.pushReplacementNamed(context, '/tabs');
}
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
elevation: 0, // 阴影
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8))),
minimumSize: const Size(double.infinity, 50),
padding: const EdgeInsets.symmetric(horizontal: 20),
),
child: Text(
_currentIndex == 0 ? '继续' : '开始进入',
style: const TextStyle(fontSize: 16, color: Colors.black),
),
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
final authProvider =
Provider.of<AuthProvider>(context, listen: false);
// 使用公开方法名
await authProvider.loadTokenFromPrefs();
if (authProvider.isLoggedIn) {
// 已登录,直接进入主页并切换到我的页面
if (!mounted) return;
Navigator.pushReplacementNamed(context, '/tabs',
arguments: {'initialIndex': 4});
} else {
// 未登录,跳转到登录页
if (!mounted) return;
Navigator.pushNamed(context, '/login');
}
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
minimumSize: const Size(double.infinity, 50),
padding: const EdgeInsets.symmetric(horizontal: 20),
side: const BorderSide(
style: BorderStyle.solid,
width: 1, // 边框宽度
color: Color(0xff73cfcf), // 默认边框颜色
)),
child: const Text(
'登录',
style: TextStyle(
color: Color(0xff73cfcf),
fontSize: 16,
),
),
),
],
),
),
],
),
);
}
}