refactor(theme&ui): improve theme switching and navigation styling

This commit is contained in:
anhtunz
2024-12-26 14:52:33 +07:00
parent a69429b05f
commit 70e3ed8978
6 changed files with 255 additions and 366 deletions

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:sfm_app/product/base/bloc/base_bloc.dart';
import 'package:sfm_app/product/constant/enums/app_theme_enums.dart';
import '../bell/bell_model.dart';
@@ -14,6 +15,10 @@ class MainBloc extends BlocBase {
final language = StreamController<Locale?>.broadcast();
StreamSink<Locale?> get sinkLanguage => language.sink;
Stream<Locale?> get streamLanguage => language.stream;
final theme = StreamController<ThemeData?>.broadcast();
StreamSink<ThemeData?> get sinkTheme => theme.sink;
Stream<ThemeData?> get streamTheme => theme.stream;
final themeMode = StreamController<bool>.broadcast();
StreamSink<bool> get sinkThemeMode => themeMode.sink;

View File

@@ -6,15 +6,12 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent-tab-view.dart';
import 'package:provider/provider.dart';
import 'package:badges/badges.dart' as badges;
import 'package:sfm_app/feature/home/home_bloc.dart';
import 'package:sfm_app/product/constant/app/app_constants.dart';
import 'package:sfm_app/product/constant/enums/app_route_enums.dart';
import 'package:sfm_app/product/constant/enums/role_enums.dart';
import 'package:sfm_app/product/permission/location_permission.dart';
import 'package:sfm_app/product/shared/shared_language_switch.dart';
import '../../product/shared/shared_light_dark_switch.dart';
import '../home/home_bloc.dart';
import '../../product/constant/app/app_constants.dart';
import '../../product/constant/enums/app_route_enums.dart';
import '../../product/permission/location_permission.dart';
import '../../product/services/theme_services.dart';
import '../devices/devices_manager_bloc.dart';
import '../devices/devices_manager_screen.dart';
import '../home/home_screen.dart';
@@ -27,13 +24,11 @@ import '../map/map_bloc.dart';
import '../map/map_screen.dart';
import '../../product/base/bloc/base_bloc.dart';
import '../../product/constant/enums/app_theme_enums.dart';
import '../../product/extention/context_extention.dart';
import '../../product/services/api_services.dart';
import '../../main.dart';
import '../../product/constant/icon/icon_constants.dart';
import '../../product/constant/lang/language_constants.dart';
import '../../product/services/language_services.dart';
import '../../product/theme/theme_notifier.dart';
import '../bell/bell_model.dart';
class MainScreen extends StatefulWidget {
@@ -69,16 +64,12 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
}
mainBloc.sinkIsVNIcon.add(isVN);
mainBloc.sinkThemeMode.add(isLight);
log("role: $role");
LocationPermissionRequest.instance.checkLocationPermission(context);
}
// For test
late bool dayNightToggle2;
@override
void initState() {
super.initState();
dayNightToggle2 = false;
mainBloc = BlocProvider.of(context);
WidgetsBinding.instance.addObserver(this);
initialCheck();
@@ -178,216 +169,199 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
ThemeNotifier themeNotifier = context.watch<ThemeNotifier>();
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
actions: [
// LightDarkSwitch(
// value: !isLight,
// onChanged: (value) {
// themeNotifier.changeTheme();
// isLight = !isLight;
// },
// ),
// SizedBox(
// width: context.lowValue,
// ),
// StreamBuilder<bool>(
// stream: mainBloc.streamIsVNIcon,
// builder: (context, isVNSnapshot) {
// return LanguageSwitch(
// value: isVNSnapshot.data ?? isVN,
// onChanged: (value) async {
// Locale locale = await LanguageServices().setLocale(isVN
// ? LanguageConstants.ENGLISH
// : LanguageConstants.VIETNAM);
// MyApp.setLocale(context, locale);
// isVN = !isVN;
// mainBloc.sinkIsVNIcon.add(isVN);
// },
// );
// }),
// SizedBox(
// width: context.lowValue,
// ),
StreamBuilder<bool>(
stream: mainBloc.streamThemeMode,
initialData: isLight,
builder: (context, themeModeSnapshot) {
return IconButton(
onPressed: () {
themeNotifier.changeTheme();
return StreamBuilder<bool>(
stream: mainBloc.streamThemeMode,
initialData: isLight,
builder: (context, themeModeSnapshot) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
actions: [
// LightDarkSwitch(
// value: !isLight,
// onChanged: (value) {
// themeNotifier.changeTheme();
// isLight = !isLight;
// },
// ),
// SizedBox(
// width: context.lowValue,
// ),
// StreamBuilder<bool>(
// stream: mainBloc.streamIsVNIcon,
// builder: (context, isVNSnapshot) {
// return LanguageSwitch(
// value: isVNSnapshot.data ?? isVN,
// onChanged: (value) async {
// Locale locale = await LanguageServices().setLocale(isVN
// ? LanguageConstants.ENGLISH
// : LanguageConstants.VIETNAM);
// MyApp.setLocale(context, locale);
// isVN = !isVN;
// mainBloc.sinkIsVNIcon.add(isVN);
// },
// );
// }),
// SizedBox(
// width: context.lowValue,
// ),
IconButton(
onPressed: () async {
ThemeData newTheme = await ThemeServices().changeTheme(
isLight ? AppThemes.DARK.name : AppThemes.LIGHT.name);
MyApp.setTheme(context, newTheme);
isLight = !isLight;
mainBloc.sinkThemeMode.add(isLight);
},
icon: Icon(
themeModeSnapshot.data ?? isLight
? Icons.light_mode_outlined
: Icons.dark_mode_outlined,
? Icons.dark_mode_outlined
: Icons.light_mode_outlined,
),
);
},
),
StreamBuilder<bool>(
stream: mainBloc.streamIsVNIcon,
initialData: isVN,
builder: (context, isVnSnapshot) {
return IconButton(
onPressed: () async {
log("Locale: ${LanguageServices().getLocale()}");
Locale locale = await LanguageServices().setLocale(isVN
? LanguageConstants.ENGLISH
: LanguageConstants.VIETNAM);
MyApp.setLocale(context, locale);
isVN = !isVN;
mainBloc.sinkIsVNIcon.add(isVN);
),
StreamBuilder<bool>(
stream: mainBloc.streamIsVNIcon,
initialData: isVN,
builder: (context, isVnSnapshot) {
return IconButton(
onPressed: () async {
log("Locale: ${LanguageServices().getLocale()}");
Locale locale = await LanguageServices().setLocale(isVN
? LanguageConstants.ENGLISH
: LanguageConstants.VIETNAM);
MyApp.setLocale(context, locale);
isVN = !isVN;
mainBloc.sinkIsVNIcon.add(isVN);
},
icon: Image.asset(
IconConstants.instance.getIcon(
isVnSnapshot.data ?? isVN ? 'vi_icon' : 'en_icon'),
height: 24,
width: 24,
),
);
},
icon: Image.asset(
IconConstants.instance.getIcon(
isVnSnapshot.data ?? isVN ? 'vi_icon' : 'en_icon'),
height: 24,
width: 24,
),
StreamBuilder<Bell>(
stream: mainBloc.streamBellBloc,
builder: (context, bellSnapshot) {
return checkStatus(bellSnapshot.data?.items ?? [])
? IconButton(
onPressed: () {
context.pushNamed(AppRoutes.BELL.name);
},
icon: const Icon(
Icons.notifications,
),
)
: GestureDetector(
child: badges.Badge(
badgeStyle: const badges.BadgeStyle(
shape: badges.BadgeShape.twitter,
),
key: _badgeKey,
badgeContent: const Icon(
CupertinoIcons.circle_filled,
color: Colors.red,
size: 5,
),
badgeAnimation: const badges.BadgeAnimation.slide(
animationDuration: Duration(milliseconds: 200),
colorChangeAnimationDuration:
Duration(seconds: 1),
loopAnimation: false,
curve: Curves.decelerate,
colorChangeAnimationCurve: Curves.easeInCirc,
),
showBadge: true,
// ignorePointer: false,
child: const Icon(
Icons.notifications,
size: 30,
),
),
onTap: () {
context.pushNamed(AppRoutes.BELL.name);
},
);
},
),
PopupMenuButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
);
},
),
StreamBuilder<Bell>(
stream: mainBloc.streamBellBloc,
builder: (context, bellSnapshot) {
return checkStatus(bellSnapshot.data?.items ?? [])
? IconButton(
onPressed: () {
context.pushNamed(AppRoutes.BELL.name);
},
icon: const Icon(
Icons.notifications,
),
)
: GestureDetector(
child: badges.Badge(
badgeStyle: const badges.BadgeStyle(
shape: badges.BadgeShape.twitter,
),
key: _badgeKey,
badgeContent: const Icon(
CupertinoIcons.circle_filled,
color: Colors.red,
size: 5,
),
badgeAnimation: const badges.BadgeAnimation.slide(
animationDuration: Duration(milliseconds: 200),
colorChangeAnimationDuration: Duration(seconds: 1),
loopAnimation: false,
curve: Curves.decelerate,
colorChangeAnimationCurve: Curves.easeInCirc,
),
showBadge: true,
// ignorePointer: false,
child: const Icon(
Icons.notifications,
size: 30,
),
),
icon: const Icon(Icons.more_vert),
itemBuilder: (context) {
return [
PopupMenuItem(
value: ApplicationConstants.SETTINGS_PATH,
onTap: () {
context.pushNamed(AppRoutes.BELL.name);
context.pushNamed(AppRoutes.SETTINGS.name);
},
);
},
),
PopupMenuButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
icon: const Icon(Icons.more_vert),
itemBuilder: (context) {
return [
PopupMenuItem(
value: ApplicationConstants.SETTINGS_PATH,
onTap: () {
context.pushNamed(AppRoutes.SETTINGS.name);
},
child: Row(
children: [
const Icon(Icons.person),
const SizedBox(width: 5),
Text(appLocalization(context).profile_icon_title)
],
),
),
PopupMenuItem(
value: ApplicationConstants.LOGOUT_PATH,
onTap: () {
Future.delayed(
const Duration(milliseconds: 200),
() async {
await apiServices.logOut(context);
},
);
},
child: Row(
children: [
const Icon(Icons.logout),
const SizedBox(width: 5),
Text(
appLocalization(context).log_out,
child: Row(
children: [
const Icon(Icons.person),
const SizedBox(width: 5),
Text(appLocalization(context).profile_icon_title)
],
),
],
),
),
];
},
)
],
),
// bottomNavigationBar: Container(
// decoration:
// BoxDecoration(borderRadius: BorderRadius.circular(50)),
// padding: context.paddingLow,
// child: NavigationBar(
// onDestinationSelected: (index) {
// currentPageIndex = index;
// mainBloc.sinkCurrentPageIndex.add(currentPageIndex);
// checkSelectedIndex(currentPageIndex);
// },
// selectedIndex: indexSnapshot.data ?? currentPageIndex,
// destinations: roleSnapshot.data == RoleEnums.USER.name
// ? userDestinations
// : modDestinations,
// ),
// ),
// body: IndexedStack(
// index: indexSnapshot.data ?? currentPageIndex,
// children: roleSnapshot.data == RoleEnums.USER.name
// ? userBody
// : modBody,
// ),
body: PersistentTabView(
context,
controller: controller,
screens: _buildScreens(),
items: _navBarsItems(),
confineInSafeArea: true,
handleAndroidBackButtonPress: true,
resizeToAvoidBottomInset: true,
stateManagement: true,
hideNavigationBarWhenKeyboardShows: true,
// backgroundColor: Colors.transparent,
decoration: NavBarDecoration(
borderRadius: BorderRadius.circular(30.0),
),
popAllScreensOnTapOfSelectedTab: true,
itemAnimationProperties: const ItemAnimationProperties(
duration: Duration(milliseconds: 200),
curve: Curves.bounceInOut,
),
screenTransitionAnimation: const ScreenTransitionAnimation(
animateTabTransition: true,
curve: Curves.linear,
duration: Duration(milliseconds: 200),
),
navBarStyle: NavBarStyle.style4,
),
),
PopupMenuItem(
value: ApplicationConstants.LOGOUT_PATH,
onTap: () {
Future.delayed(
const Duration(milliseconds: 200),
() async {
await apiServices.logOut(context);
},
);
},
child: Row(
children: [
const Icon(Icons.logout),
const SizedBox(width: 5),
Text(
appLocalization(context).log_out,
),
],
),
),
];
},
)
],
),
body: PersistentTabView(
context,
controller: controller,
screens: _buildScreens(),
items: _navBarsItems(),
confineInSafeArea: true,
handleAndroidBackButtonPress: true,
resizeToAvoidBottomInset: true,
stateManagement: true,
hideNavigationBarWhenKeyboardShows: true,
backgroundColor:
themeModeSnapshot.data! ? Colors.white : Colors.black,
decoration: NavBarDecoration(
borderRadius: BorderRadius.circular(30.0),
colorBehindNavBar:
themeModeSnapshot.data! ? Colors.white : Colors.black,
),
popAllScreensOnTapOfSelectedTab: true,
itemAnimationProperties: const ItemAnimationProperties(
duration: Duration(milliseconds: 200),
curve: Curves.bounceInOut,
),
screenTransitionAnimation: const ScreenTransitionAnimation(
animateTabTransition: true,
curve: Curves.linear,
duration: Duration(milliseconds: 200),
),
navBarStyle: NavBarStyle.style4,
),
);
},
);
}