Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unittest and home screen refactoring #31

Merged
merged 10 commits into from
Nov 21, 2024
5 changes: 5 additions & 0 deletions lib/configuration/app_key_constants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'package:flutter/material.dart';

const Key drawerButtonLogout = Key("drawerButtonLogout");
const Key drawerButtonLogoutYes = Key("drawerButtonLogoutYes");
const Key drawerButtonLogoutNo = Key("drawerButtonLogoutNo");
1 change: 1 addition & 0 deletions lib/configuration/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ class LocaleConstants {
static const Map<String, String> languages = {'en': 'English'};
static const String langStorageKey = 'locale';
static const String logoLightUrl = 'assets/images/logoLight.png';
static const String defaultImgUrl = 'assets/images/img.png';
}
2 changes: 1 addition & 1 deletion lib/data/repository/account_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class AccountRepository {
final httpResponse = await HttpUtils.getRequest("/$_resource");
var response = HttpUtils.decodeUTF8(httpResponse.body.toString());
var result = User.fromJsonString(response)!;
debugPrint("END:getAccount successful - response : $response}");
debugPrint("END:getAccount successful - response : ${response.length}}");
return result;
}

Expand Down
168 changes: 95 additions & 73 deletions lib/main/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,87 +45,109 @@ import '../presentation/screen/user/list/list_user_screen.dart';

class App extends StatelessWidget {
final String language;
final AdaptiveThemeMode initialTheme;

const App({super.key, required this.language});
App({super.key, required this.language, required this.initialTheme});

@override
Widget build(BuildContext context) {
return buildHomeApp();
}

AdaptiveTheme buildHomeApp() {
return AdaptiveTheme(
light: ThemeData(
useMaterial3: false,
brightness: Brightness.light,
colorSchemeSeed: Colors.blueGrey,
),
dark: ThemeData(
useMaterial3: false,
brightness: Brightness.dark,
primarySwatch: Colors.blueGrey,
),
light: _buildLightTheme(),
dark: _buildDarkTheme(),
debugShowFloatingThemeButton: false,
initial: AdaptiveThemeMode.dark,
initial: initialTheme,
builder: (light, dark) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthorityBloc>(create: (_) => AuthorityBloc(authorityRepository: AuthorityRepository())),
BlocProvider<AccountBloc>(create: (_) => AccountBloc(accountRepository: AccountRepository())),
BlocProvider<UserBloc>(create: (_) => UserBloc(userRepository: UserRepository())),
BlocProvider<CityBloc>(create: (_) => CityBloc(cityRepository: CityRepository())),
BlocProvider<DistrictBloc>(create: (_) => DistrictBloc(districtRepository: DistrictRepository())),
BlocProvider<DrawerBloc>(create: (_) => DrawerBloc(loginRepository: LoginRepository(), menuRepository: MenuRepository())),
],
child: GetMaterialApp(
theme: light,
darkTheme: dark,
debugShowCheckedModeBanner: ProfileConstants.isDevelopment,
debugShowMaterialGrid: false,
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
locale: Locale(language),
initialRoute: initialRouteControl(),
routes: {
ApplicationRoutes.home: (context) {
return BlocProvider<AccountBloc>(
create: (context) => AccountBloc(accountRepository: AccountRepository())..add(const AccountLoad()), child: HomeScreen());
},
ApplicationRoutes.account: (context) {
return BlocProvider<AccountBloc>(
create: (context) => AccountBloc(accountRepository: AccountRepository())..add(const AccountLoad()), child: AccountsScreen());
},
ApplicationRoutes.login: (context) {
return BlocProvider<LoginBloc>(create: (context) => LoginBloc(loginRepository: LoginRepository()), child: LoginScreen());
},
ApplicationRoutes.settings: (context) {
return BlocProvider<SettingsBloc>(
create: (context) => SettingsBloc(accountRepository: AccountRepository()), child: const SettingsScreen());
},
ApplicationRoutes.forgotPassword: (context) {
return BlocProvider<ForgotPasswordBloc>(
create: (context) => ForgotPasswordBloc(accountRepository: AccountRepository()), child: ForgotPasswordScreen());
},
ApplicationRoutes.register: (context) {
return BlocProvider<RegisterBloc>(
create: (context) => RegisterBloc(accountRepository: AccountRepository()), child: RegisterScreen());
},
ApplicationRoutes.changePassword: (context) {
return BlocProvider<ChangePasswordBloc>(
create: (context) => ChangePasswordBloc(accountRepository: AccountRepository()), child: ChangePasswordScreen());
},
ApplicationRoutes.logout: (context) => const LogoutConfirmationDialog(),
ApplicationRoutes.createUser: (context) {
return BlocProvider<UserBloc>(create: (context) => UserBloc(userRepository: UserRepository()), child: CreateUserScreen());
},
ApplicationRoutes.listUsers: (context) {
return BlocProvider<UserBloc>(create: (context) => UserBloc(userRepository: UserRepository()), child: ListUserScreen());
},
},
),
);
return _buildMultiBlocProvider(light, dark);
},
);
}

ThemeData _buildDarkTheme() {
return ThemeData(
useMaterial3: false,
brightness: Brightness.dark,
primarySwatch: Colors.blueGrey,
);
}

ThemeData _buildLightTheme() {
return ThemeData(
useMaterial3: false,
brightness: Brightness.light,
colorSchemeSeed: Colors.blueGrey,
);
}

MultiBlocProvider _buildMultiBlocProvider(ThemeData light, ThemeData dark) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthorityBloc>(create: (_) => AuthorityBloc(authorityRepository: AuthorityRepository())),
BlocProvider<AccountBloc>(create: (_) => AccountBloc(accountRepository: AccountRepository())),
BlocProvider<UserBloc>(create: (_) => UserBloc(userRepository: UserRepository())),
BlocProvider<CityBloc>(create: (_) => CityBloc(cityRepository: CityRepository())),
BlocProvider<DistrictBloc>(create: (_) => DistrictBloc(districtRepository: DistrictRepository())),
BlocProvider<DrawerBloc>(create: (_) => DrawerBloc(loginRepository: LoginRepository(), menuRepository: MenuRepository())),
],
child: _buildGetMaterialApp(light, dark),
);
}

GetMaterialApp _buildGetMaterialApp(ThemeData light, ThemeData dark) {
return GetMaterialApp(
theme: light,
darkTheme: dark,
debugShowCheckedModeBanner: ProfileConstants.isDevelopment,
debugShowMaterialGrid: false,
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
locale: Locale(language),
initialRoute: initialRouteControl(),
routes: _initialRoutes,
);
}

final _initialRoutes = {
ApplicationRoutes.home: (context) {
return BlocProvider<AccountBloc>(
create: (context) => AccountBloc(accountRepository: AccountRepository())..add(const AccountLoad()), child: HomeScreen());
},
ApplicationRoutes.account: (context) {
return BlocProvider<AccountBloc>(
create: (context) => AccountBloc(accountRepository: AccountRepository())..add(const AccountLoad()), child: AccountsScreen());
},
ApplicationRoutes.login: (context) {
return BlocProvider<LoginBloc>(create: (context) => LoginBloc(loginRepository: LoginRepository()), child: LoginScreen());
},
ApplicationRoutes.settings: (context) {
return BlocProvider<SettingsBloc>(
create: (context) => SettingsBloc(accountRepository: AccountRepository()), child: const SettingsScreen());
},
ApplicationRoutes.forgotPassword: (context) {
return BlocProvider<ForgotPasswordBloc>(
create: (context) => ForgotPasswordBloc(accountRepository: AccountRepository()), child: ForgotPasswordScreen());
},
ApplicationRoutes.register: (context) {
return BlocProvider<RegisterBloc>(create: (context) => RegisterBloc(accountRepository: AccountRepository()), child: RegisterScreen());
},
ApplicationRoutes.changePassword: (context) {
return BlocProvider<ChangePasswordBloc>(
create: (context) => ChangePasswordBloc(accountRepository: AccountRepository()), child: ChangePasswordScreen());
},
ApplicationRoutes.logout: (context) => const LogoutConfirmationDialog(),
ApplicationRoutes.createUser: (context) {
return BlocProvider<UserBloc>(create: (context) => UserBloc(userRepository: UserRepository()), child: CreateUserScreen());
},
ApplicationRoutes.listUsers: (context) {
return BlocProvider<UserBloc>(create: (context) => UserBloc(userRepository: UserRepository()), child: ListUserScreen());
},
};
}
4 changes: 3 additions & 1 deletion lib/main/main_local.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc_advance/configuration/local_storage.dart';
Expand All @@ -16,10 +17,11 @@ void main() async {
initializeJsonMapper();
WidgetsFlutterBinding.ensureInitialized();
const defaultLanguage = "en";
const initialTheme = AdaptiveThemeMode.dark;
await AppLocalStorage().save(StorageKeys.language.name, defaultLanguage);

WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((_) {
runApp(const App(language: defaultLanguage));
runApp(App(language: defaultLanguage, initialTheme: initialTheme));
});
}
4 changes: 3 additions & 1 deletion lib/main/main_prod.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc_advance/configuration/environment.dart';
Expand All @@ -17,10 +18,11 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();

const defaultLanguage = "en";
const initialTheme = AdaptiveThemeMode.dark;
await AppLocalStorage().save(StorageKeys.language.name, defaultLanguage);

WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((_) {
runApp(const App(language: defaultLanguage));
runApp(App(language: defaultLanguage, initialTheme: initialTheme));
});
}
1 change: 1 addition & 0 deletions lib/presentation/common_blocs/account/account_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
try {
User user = await _accountRepository.getAccount();
await AppLocalStorage().save(StorageKeys.roles.name, user.authorities);
await AppLocalStorage().save(StorageKeys.username.name, user.login);

emit(state.copyWith(account: user, status: AccountStatus.success));
log("AccountBloc._onLoad end : ${state.account}, ${state.status}");
Expand Down
18 changes: 11 additions & 7 deletions lib/presentation/common_widgets/drawer/drawer_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:expansion_tile_card/expansion_tile_card.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bloc_advance/configuration/app_key_constants.dart';
import 'package:flutter_bloc_advance/configuration/local_storage.dart';
import 'package:flutter_bloc_advance/utils/security_utils.dart';
import 'package:string_2_icon/string_2_icon.dart';
Expand Down Expand Up @@ -55,6 +56,7 @@ class ApplicationDrawer extends StatelessWidget {
child: SizedBox(
width: double.infinity,
child: ElevatedButton(
key: drawerButtonLogout,
style: ElevatedButton.styleFrom(elevation: 0),
onPressed: () => logOutDialog(context),
child: Text(S.of(context).logout, textAlign: TextAlign.center),
Expand Down Expand Up @@ -83,13 +85,13 @@ class ApplicationDrawer extends StatelessWidget {

ListTile _buildMenuListListTile(List<dynamic> parentMenus, int index, BuildContext context) {
return ListTile(
leading: Icon(String2Icon.getIconDataFromString(parentMenus[index].icon)),
title: Text(S.of(context).translate_menu_title(parentMenus[index].name), style: Theme.of(context).textTheme.bodyMedium),
onTap: () {
Navigator.pop(context);
Navigator.pushNamed(context, parentMenus[index].url);
},
);
leading: Icon(String2Icon.getIconDataFromString(parentMenus[index].icon)),
title: Text(S.of(context).translate_menu_title(parentMenus[index].name), style: Theme.of(context).textTheme.bodyMedium),
onTap: () {
Navigator.pop(context);
Navigator.pushNamed(context, parentMenus[index].url);
},
);
}

ExpansionTileCard _buildMenuListUserManagement(DrawerState state, List<dynamic> parentMenus, int index, BuildContext context) {
Expand Down Expand Up @@ -168,10 +170,12 @@ class ApplicationDrawer extends StatelessWidget {
content: Text(S.of(context).logout_sure),
actions: [
TextButton(
key: drawerButtonLogoutYes,
onPressed: () => onLogout(context),
child: Text(S.of(context).yes),
),
TextButton(
key: drawerButtonLogoutNo,
onPressed: () => onCancel(context),
child: Text(S.of(context).no),
),
Expand Down
9 changes: 5 additions & 4 deletions lib/presentation/screen/home/home_screen.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bloc_advance/configuration/constants.dart';
import 'package:flutter_bloc_advance/utils/app_constants.dart';

import '../../../configuration/routes.dart';
import '../../../data/repository/login_repository.dart';
import '../../../data/repository/menu_repository.dart';
import '../../../generated/l10n.dart';
import '../../common_blocs/account/account.dart';
import '../../common_widgets/drawer/drawer_bloc/drawer_bloc.dart';
import '../../common_widgets/drawer/drawer_widget.dart';
Expand All @@ -32,7 +33,7 @@ class HomeScreen extends StatelessWidget {
builder: (context, state) {
if (state.status == AccountStatus.success) {
return Scaffold(
appBar: AppBar(title: Text(S.of(context).description)),
appBar: AppBar(title: const Text(AppConstants.appName)),
key: _scaffoldKey,
body: Center(child: Column(children: [backgroundImage(context)])),
drawer: _buildDrawer(context),
Expand All @@ -54,7 +55,7 @@ class HomeScreen extends StatelessWidget {
height: 300,
width: 300,
decoration: const BoxDecoration(
image: DecorationImage(image: AssetImage("assets/images/logoLight.png"), scale: 1, fit: BoxFit.contain),
image: DecorationImage(image: AssetImage(LocaleConstants.logoLightUrl), scale: 1, fit: BoxFit.contain),
),
),
),
Expand All @@ -68,7 +69,7 @@ class HomeScreen extends StatelessWidget {
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: const AssetImage("assets/images/img.png"),
image: const AssetImage(LocaleConstants.defaultImgUrl),
colorFilter: ColorFilter.mode(
AdaptiveTheme.of(context).mode.isDark ? Colors.black.withOpacity(0.1) : Colors.white.withOpacity(0.1), BlendMode.dstIn),
),
Expand Down
Loading
Loading