Add application source code and update project structure

This commit is contained in:
PhongMacbook
2025-11-05 03:20:59 +07:00
parent 95f8296211
commit b145c7844f
155 changed files with 9171 additions and 0 deletions

View File

@@ -0,0 +1,293 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:page_transition/page_transition.dart';
import 'package:provider/provider.dart';
import '/index.dart';
import '/main.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/lat_lng.dart';
import '/flutter_flow/place.dart';
import '/flutter_flow/flutter_flow_util.dart';
import 'serialization_util.dart';
export 'package:go_router/go_router.dart';
export 'serialization_util.dart';
const kTransitionInfoKey = '__transition_info__';
class AppStateNotifier extends ChangeNotifier {
AppStateNotifier._();
static AppStateNotifier? _instance;
static AppStateNotifier get instance => _instance ??= AppStateNotifier._();
bool showSplashImage = true;
void stopShowingSplashImage() {
showSplashImage = false;
notifyListeners();
}
}
GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter(
initialLocation: '/',
debugLogDiagnostics: true,
refreshListenable: appStateNotifier,
errorBuilder: (context, state) => appStateNotifier.showSplashImage
? Builder(
builder: (context) => Container(
color: Colors.transparent,
child: Image.asset(
'assets/images/Settings_(4).png',
fit: BoxFit.cover,
),
),
)
: NavBarPage(),
routes: [
FFRoute(
name: '_initialize',
path: '/',
builder: (context, _) => appStateNotifier.showSplashImage
? Builder(
builder: (context) => Container(
color: Colors.transparent,
child: Image.asset(
'assets/images/Settings_(4).png',
fit: BoxFit.cover,
),
),
)
: NavBarPage(),
),
FFRoute(
name: 'HomePage',
path: '/homePage',
builder: (context, params) => params.isEmpty
? NavBarPage(initialPage: 'HomePage')
: HomePageWidget(),
),
FFRoute(
name: 'Analyst',
path: '/analyst',
builder: (context, params) => params.isEmpty
? NavBarPage(initialPage: 'Analyst')
: AnalystWidget(),
),
FFRoute(
name: 'Infomation',
path: '/infomation',
builder: (context, params) => params.isEmpty
? NavBarPage(initialPage: 'Infomation')
: InfomationWidget(),
),
FFRoute(
name: 'setting',
path: '/setting',
builder: (context, params) => params.isEmpty
? NavBarPage(initialPage: 'setting')
: SettingWidget(),
),
FFRoute(
name: 'NEWS',
path: '/news',
builder: (context, params) =>
params.isEmpty ? NavBarPage(initialPage: 'NEWS') : NewsWidget(),
)
].map((r) => r.toRoute(appStateNotifier)).toList(),
);
extension NavParamExtensions on Map<String, String?> {
Map<String, String> get withoutNulls => Map.fromEntries(
entries
.where((e) => e.value != null)
.map((e) => MapEntry(e.key, e.value!)),
);
}
extension NavigationExtensions on BuildContext {
void safePop() {
// If there is only one route on the stack, navigate to the initial
// page instead of popping.
if (canPop()) {
pop();
} else {
go('/');
}
}
}
extension _GoRouterStateExtensions on GoRouterState {
Map<String, dynamic> get extraMap =>
extra != null ? extra as Map<String, dynamic> : {};
Map<String, dynamic> get allParams => <String, dynamic>{}
..addAll(pathParameters)
..addAll(uri.queryParameters)
..addAll(extraMap);
TransitionInfo get transitionInfo => extraMap.containsKey(kTransitionInfoKey)
? extraMap[kTransitionInfoKey] as TransitionInfo
: TransitionInfo.appDefault();
}
class FFParameters {
FFParameters(this.state, [this.asyncParams = const {}]);
final GoRouterState state;
final Map<String, Future<dynamic> Function(String)> asyncParams;
Map<String, dynamic> futureParamValues = {};
// Parameters are empty if the params map is empty or if the only parameter
// present is the special extra parameter reserved for the transition info.
bool get isEmpty =>
state.allParams.isEmpty ||
(state.allParams.length == 1 &&
state.extraMap.containsKey(kTransitionInfoKey));
bool isAsyncParam(MapEntry<String, dynamic> param) =>
asyncParams.containsKey(param.key) && param.value is String;
bool get hasFutures => state.allParams.entries.any(isAsyncParam);
Future<bool> completeFutures() => Future.wait(
state.allParams.entries.where(isAsyncParam).map(
(param) async {
final doc = await asyncParams[param.key]!(param.value)
.onError((_, __) => null);
if (doc != null) {
futureParamValues[param.key] = doc;
return true;
}
return false;
},
),
).onError((_, __) => [false]).then((v) => v.every((e) => e));
dynamic getParam<T>(
String paramName,
ParamType type, {
bool isList = false,
}) {
if (futureParamValues.containsKey(paramName)) {
return futureParamValues[paramName];
}
if (!state.allParams.containsKey(paramName)) {
return null;
}
final param = state.allParams[paramName];
// Got parameter from `extras`, so just directly return it.
if (param is! String) {
return param;
}
// Return serialized value.
return deserializeParam<T>(
param,
type,
isList,
);
}
}
class FFRoute {
const FFRoute({
required this.name,
required this.path,
required this.builder,
this.requireAuth = false,
this.asyncParams = const {},
this.routes = const [],
});
final String name;
final String path;
final bool requireAuth;
final Map<String, Future<dynamic> Function(String)> asyncParams;
final Widget Function(BuildContext, FFParameters) builder;
final List<GoRoute> routes;
GoRoute toRoute(AppStateNotifier appStateNotifier) => GoRoute(
name: name,
path: path,
pageBuilder: (context, state) {
fixStatusBarOniOS16AndBelow(context);
final ffParams = FFParameters(state, asyncParams);
final page = ffParams.hasFutures
? FutureBuilder(
future: ffParams.completeFutures(),
builder: (context, _) => builder(context, ffParams),
)
: builder(context, ffParams);
final child = page;
final transitionInfo = state.transitionInfo;
return transitionInfo.hasTransition
? CustomTransitionPage(
key: state.pageKey,
child: child,
transitionDuration: transitionInfo.duration,
transitionsBuilder:
(context, animation, secondaryAnimation, child) =>
PageTransition(
type: transitionInfo.transitionType,
duration: transitionInfo.duration,
reverseDuration: transitionInfo.duration,
alignment: transitionInfo.alignment,
child: child,
).buildTransitions(
context,
animation,
secondaryAnimation,
child,
),
)
: MaterialPage(key: state.pageKey, child: child);
},
routes: routes,
);
}
class TransitionInfo {
const TransitionInfo({
required this.hasTransition,
this.transitionType = PageTransitionType.fade,
this.duration = const Duration(milliseconds: 300),
this.alignment,
});
final bool hasTransition;
final PageTransitionType transitionType;
final Duration duration;
final Alignment? alignment;
static TransitionInfo appDefault() => TransitionInfo(hasTransition: false);
}
class RootPageContext {
const RootPageContext(this.isRootPage, [this.errorRoute]);
final bool isRootPage;
final String? errorRoute;
static bool isInactiveRootPage(BuildContext context) {
final rootPageContext = context.read<RootPageContext?>();
final isRootPage = rootPageContext?.isRootPage ?? false;
final location = GoRouterState.of(context).uri.toString();
return isRootPage &&
location != '/' &&
location != rootPageContext?.errorRoute;
}
static Widget wrap(Widget child, {String? errorRoute}) => Provider.value(
value: RootPageContext(true, errorRoute),
child: child,
);
}
extension GoRouterLocationExtension on GoRouter {
String getCurrentLocation() {
final RouteMatch lastMatch = routerDelegate.currentConfiguration.last;
final RouteMatchList matchList = lastMatch is ImperativeRouteMatch
? lastMatch.matches
: routerDelegate.currentConfiguration;
return matchList.uri.toString();
}
}

View File

@@ -0,0 +1,206 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:from_css_color/from_css_color.dart';
import '../../flutter_flow/lat_lng.dart';
import '../../flutter_flow/place.dart';
import '../../flutter_flow/uploaded_file.dart';
/// SERIALIZATION HELPERS
String dateTimeRangeToString(DateTimeRange dateTimeRange) {
final startStr = dateTimeRange.start.millisecondsSinceEpoch.toString();
final endStr = dateTimeRange.end.millisecondsSinceEpoch.toString();
return '$startStr|$endStr';
}
String placeToString(FFPlace place) => jsonEncode({
'latLng': place.latLng.serialize(),
'name': place.name,
'address': place.address,
'city': place.city,
'state': place.state,
'country': place.country,
'zipCode': place.zipCode,
});
String uploadedFileToString(FFUploadedFile uploadedFile) =>
uploadedFile.serialize();
String? serializeParam(
dynamic param,
ParamType paramType, {
bool isList = false,
}) {
try {
if (param == null) {
return null;
}
if (isList) {
final serializedValues = (param as Iterable)
.map((p) => serializeParam(p, paramType, isList: false))
.where((p) => p != null)
.map((p) => p!)
.toList();
return json.encode(serializedValues);
}
String? data;
switch (paramType) {
case ParamType.int:
data = param.toString();
case ParamType.double:
data = param.toString();
case ParamType.String:
data = param;
case ParamType.bool:
data = param ? 'true' : 'false';
case ParamType.DateTime:
data = (param as DateTime).millisecondsSinceEpoch.toString();
case ParamType.DateTimeRange:
data = dateTimeRangeToString(param as DateTimeRange);
case ParamType.LatLng:
data = (param as LatLng).serialize();
case ParamType.Color:
data = (param as Color).toCssString();
case ParamType.FFPlace:
data = placeToString(param as FFPlace);
case ParamType.FFUploadedFile:
data = uploadedFileToString(param as FFUploadedFile);
case ParamType.JSON:
data = json.encode(param);
default:
data = null;
}
return data;
} catch (e) {
print('Error serializing parameter: $e');
return null;
}
}
/// END SERIALIZATION HELPERS
/// DESERIALIZATION HELPERS
DateTimeRange? dateTimeRangeFromString(String dateTimeRangeStr) {
final pieces = dateTimeRangeStr.split('|');
if (pieces.length != 2) {
return null;
}
return DateTimeRange(
start: DateTime.fromMillisecondsSinceEpoch(int.parse(pieces.first)),
end: DateTime.fromMillisecondsSinceEpoch(int.parse(pieces.last)),
);
}
LatLng? latLngFromString(String? latLngStr) {
final pieces = latLngStr?.split(',');
if (pieces == null || pieces.length != 2) {
return null;
}
return LatLng(
double.parse(pieces.first.trim()),
double.parse(pieces.last.trim()),
);
}
FFPlace placeFromString(String placeStr) {
final serializedData = jsonDecode(placeStr) as Map<String, dynamic>;
final data = {
'latLng': serializedData.containsKey('latLng')
? latLngFromString(serializedData['latLng'] as String)
: const LatLng(0.0, 0.0),
'name': serializedData['name'] ?? '',
'address': serializedData['address'] ?? '',
'city': serializedData['city'] ?? '',
'state': serializedData['state'] ?? '',
'country': serializedData['country'] ?? '',
'zipCode': serializedData['zipCode'] ?? '',
};
return FFPlace(
latLng: data['latLng'] as LatLng,
name: data['name'] as String,
address: data['address'] as String,
city: data['city'] as String,
state: data['state'] as String,
country: data['country'] as String,
zipCode: data['zipCode'] as String,
);
}
FFUploadedFile uploadedFileFromString(String uploadedFileStr) =>
FFUploadedFile.deserialize(uploadedFileStr);
enum ParamType {
int,
double,
String,
bool,
DateTime,
DateTimeRange,
LatLng,
Color,
FFPlace,
FFUploadedFile,
JSON,
}
dynamic deserializeParam<T>(
String? param,
ParamType paramType,
bool isList,
) {
try {
if (param == null) {
return null;
}
if (isList) {
final paramValues = json.decode(param);
if (paramValues is! Iterable || paramValues.isEmpty) {
return null;
}
return paramValues
.where((p) => p is String)
.map((p) => p as String)
.map((p) => deserializeParam<T>(p, paramType, false))
.where((p) => p != null)
.map((p) => p! as T)
.toList();
}
switch (paramType) {
case ParamType.int:
return int.tryParse(param);
case ParamType.double:
return double.tryParse(param);
case ParamType.String:
return param;
case ParamType.bool:
return param == 'true';
case ParamType.DateTime:
final milliseconds = int.tryParse(param);
return milliseconds != null
? DateTime.fromMillisecondsSinceEpoch(milliseconds)
: null;
case ParamType.DateTimeRange:
return dateTimeRangeFromString(param);
case ParamType.LatLng:
return latLngFromString(param);
case ParamType.Color:
return fromCssColor(param);
case ParamType.FFPlace:
return placeFromString(param);
case ParamType.FFUploadedFile:
return uploadedFileFromString(param);
case ParamType.JSON:
return json.decode(param);
default:
return null;
}
} catch (e) {
print('Error deserializing parameter: $e');
return null;
}
}