Skip to content

Commit 088ebcc

Browse files
authored
feat: add state management, routing (#201)
* wip * feat: update services , repositories, add models * add AuthenticationRepository, clean up * wip * add auth and login blocs * Update project.dic * feat: add app routes * Update bootstrap.dart * wip * update pages, integration tests, add routes * Update login_form.dart
1 parent d38777c commit 088ebcc

File tree

63 files changed

+1262
-258
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1262
-258
lines changed

.config/dictionaries/project.dic

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
aarch
21
aapt
2+
aarch
33
addrr
44
adminer
55
androidx
@@ -32,6 +32,8 @@ drep
3232
dreps
3333
encryptor
3434
fontawesome
35+
formz
36+
Formz
3537
gapless
3638
gcloud
3739
genhtml

catalyst_voices/integration_test/scenarios/login_scenario.dart

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ void main() {
1313
(tester) async {
1414
loginRobot = await _configure(tester);
1515

16-
await loginRobot.enterUsername('Not Valid');
16+
await loginRobot.enterEmail('Not Valid');
1717
await loginRobot.tapLoginButton();
1818
await loginRobot.checkInvalidCredentialsMessageIsShown();
1919
});
2020

21-
testWidgets('authenticates a user with an username and password',
21+
testWidgets('authenticates a user with an email and password',
2222
(tester) async {
2323
loginRobot = await _configure(tester);
2424

25-
await loginRobot.enterUsername('robot');
26-
await loginRobot.enterPassword('1234');
25+
await loginRobot.enterEmail('[email protected]');
26+
await loginRobot.enterPassword('MyPass123');
2727
await loginRobot.tapLoginButton();
2828
});
2929
});
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import 'package:catalyst_voices/dummy/constants.dart';
1+
import 'package:catalyst_voices/pages/login/login.dart';
2+
import 'package:catalyst_voices/pages/widgets/widgets.dart';
23
import 'package:flutter_test/flutter_test.dart';
34

45
final class LoginRobot {
@@ -9,36 +10,34 @@ final class LoginRobot {
910
});
1011

1112
Future<void> checkInvalidCredentialsMessageIsShown() async {
12-
final loginErrorSnackbar = find.byKey(WidgetKeys.loginErrorSnackbar);
13+
final loginErrorSnackbar = find.byKey(LoginForm.loginErrorSnackbarKey);
1314
expect(loginErrorSnackbar, findsOneWidget);
1415
await widgetTester.pump();
1516
}
1617

17-
Future<void> enterPassword(String password) async {
18-
final passwordTextController =
19-
find.byKey(WidgetKeys.passwordTextController);
20-
expect(passwordTextController, findsOneWidget);
21-
await widgetTester.enterText(passwordTextController, password);
18+
Future<void> enterEmail(String email) async {
19+
final emailTextField = find.byKey(EmailInput.emailInputKey);
20+
expect(emailTextField, findsOneWidget);
21+
await widgetTester.enterText(emailTextField, email);
2222
await widgetTester.pump();
2323
}
2424

25-
Future<void> enterUsername(String username) async {
26-
final usernameTextController =
27-
find.byKey(WidgetKeys.usernameTextController);
28-
expect(usernameTextController, findsOneWidget);
29-
await widgetTester.enterText(usernameTextController, username);
25+
Future<void> enterPassword(String password) async {
26+
final passwordTextField = find.byKey(PasswordInput.passwordInputKey);
27+
expect(passwordTextField, findsOneWidget);
28+
await widgetTester.enterText(passwordTextField, password);
3029
await widgetTester.pump();
3130
}
3231

3332
Future<void> tapLoginButton() async {
34-
final loginButton = find.byKey(WidgetKeys.loginButton);
33+
final loginButton = find.byKey(LoginInButton.loginButtonKey);
3534
expect(loginButton, findsOneWidget);
3635
await widgetTester.tap(loginButton);
3736
await widgetTester.pump();
3837
}
3938

4039
void verifyLoginScreenIsShown() {
41-
final loginScreen = find.byKey(WidgetKeys.loginScreen);
40+
final loginScreen = find.byKey(LoginForm.loginFormKey);
4241
expect(loginScreen, findsOneWidget);
4342
}
4443
}

catalyst_voices/lib/app/view/app.dart

+2-22
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,2 @@
1-
import 'package:catalyst_voices/dummy/dummy.dart';
2-
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
3-
import 'package:flutter/material.dart';
4-
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
5-
6-
final class App extends StatelessWidget {
7-
const App({super.key});
8-
9-
@override
10-
Widget build(BuildContext context) {
11-
return MaterialApp(
12-
restorationScopeId: 'rootVoices',
13-
localizationsDelegates: const [
14-
...VoicesLocalizations.localizationsDelegates,
15-
LocaleNamesLocalizationsDelegate(),
16-
],
17-
supportedLocales: VoicesLocalizations.supportedLocales,
18-
localeListResolutionCallback: basicLocaleListResolution,
19-
home: isUserLoggedIn ? const HomeScreen() : const LoginPage(),
20-
);
21-
}
22-
}
1+
export 'app_content.dart';
2+
export 'app_page.dart';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'package:catalyst_voices/routes/routes.dart' show AppRouter;
2+
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
3+
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
4+
import 'package:flutter/material.dart';
5+
import 'package:flutter_bloc/flutter_bloc.dart';
6+
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
7+
8+
final class AppContent extends StatelessWidget {
9+
const AppContent({super.key});
10+
11+
@override
12+
Widget build(BuildContext context) {
13+
return BlocListener<AuthenticationBloc, AuthenticationState>(
14+
listener: (context, state) {},
15+
child: MaterialApp.router(
16+
restorationScopeId: 'rootVoices',
17+
localizationsDelegates: const [
18+
...VoicesLocalizations.localizationsDelegates,
19+
LocaleNamesLocalizationsDelegate(),
20+
],
21+
supportedLocales: VoicesLocalizations.supportedLocales,
22+
localeListResolutionCallback: basicLocaleListResolution,
23+
routerConfig: AppRouter.init(
24+
authenticationBloc: context.read<AuthenticationBloc>(),
25+
),
26+
title: 'Catalyst Voices',
27+
theme: ThemeData(
28+
useMaterial3: true,
29+
brightness: Brightness.dark,
30+
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
31+
type: BottomNavigationBarType.fixed,
32+
),
33+
),
34+
),
35+
);
36+
}
37+
}
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import 'package:catalyst_voices/app/view/app_content.dart';
2+
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
3+
import 'package:catalyst_voices_repositories/catalyst_voices_repositories.dart';
4+
import 'package:catalyst_voices_services/catalyst_voices_services.dart';
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter_bloc/flutter_bloc.dart';
7+
8+
final class App extends StatefulWidget {
9+
const App({super.key});
10+
11+
@override
12+
State<App> createState() => _AppState();
13+
}
14+
15+
final class _AppState extends State<App> {
16+
late final AuthenticationRepository _authenticationRepository;
17+
late final CredentialsStorageRepository _credentialsStorageRepository;
18+
19+
@override
20+
Widget build(BuildContext context) {
21+
return MultiRepositoryProvider(
22+
providers: [
23+
RepositoryProvider.value(
24+
value: _authenticationRepository,
25+
),
26+
],
27+
child: BlocProvider(
28+
create: (_) => AuthenticationBloc(
29+
authenticationRepository: _authenticationRepository,
30+
),
31+
child: const AppContent(),
32+
),
33+
);
34+
}
35+
36+
@override
37+
Future<void> dispose() async {
38+
await _authenticationRepository.dispose();
39+
40+
super.dispose();
41+
}
42+
43+
@override
44+
void initState() {
45+
super.initState();
46+
47+
_configureRepositories();
48+
}
49+
50+
void _configureRepositories() {
51+
_credentialsStorageRepository = CredentialsStorageRepository(
52+
secureStorageService: SecureStorageService(),
53+
);
54+
55+
_authenticationRepository = AuthenticationRepository(
56+
credentialsStorageRepository: _credentialsStorageRepository,
57+
);
58+
}
59+
}

catalyst_voices/lib/configs/bootstrap.dart

+5
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@ import 'dart:developer';
44
import 'package:catalyst_voices/configs/app_bloc_observer.dart';
55
import 'package:flutter/widgets.dart';
66
import 'package:flutter_bloc/flutter_bloc.dart';
7+
import 'package:go_router/go_router.dart';
8+
import 'package:url_strategy/url_strategy.dart';
79

810
Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
911
WidgetsFlutterBinding.ensureInitialized();
1012

13+
GoRouter.optionURLReflectsImperativeAPIs = true;
14+
setPathUrlStrategy();
15+
1116
FlutterError.onError = (details) {
1217
log(
1318
details.exceptionAsString(),

catalyst_voices/lib/dummy/constants.dart

-12
This file was deleted.

catalyst_voices/lib/dummy/dummy.dart

-8
This file was deleted.

catalyst_voices/lib/dummy/login_page.dart

-121
This file was deleted.

catalyst_voices/lib/dummy/home_screen.dart catalyst_voices/lib/pages/home/home_page.dart

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import 'package:catalyst_voices/dummy/dummy.dart';
21
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
32
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
43
import 'package:flutter/material.dart';
54

6-
final class HomeScreen extends StatelessWidget {
7-
const HomeScreen({super.key});
5+
final class HomePage extends StatelessWidget {
6+
static const homePageKey = Key('HomePage');
7+
8+
const HomePage({super.key});
89

910
@override
1011
Widget build(BuildContext context) {
1112
return Scaffold(
12-
key: WidgetKeys.homeScreen,
13+
key: homePageKey,
1314
body: Center(
1415
child: Column(
1516
mainAxisAlignment: MainAxisAlignment.center,

0 commit comments

Comments
 (0)