Skip to content

Commit

Permalink
feat: add state management, routing (#201)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
minikin authored Jan 8, 2024
1 parent d38777c commit 088ebcc
Show file tree
Hide file tree
Showing 63 changed files with 1,262 additions and 258 deletions.
4 changes: 3 additions & 1 deletion .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
aarch
aapt
aarch
addrr
adminer
androidx
Expand Down Expand Up @@ -32,6 +32,8 @@ drep
dreps
encryptor
fontawesome
formz
Formz
gapless
gcloud
genhtml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ void main() {
(tester) async {
loginRobot = await _configure(tester);

await loginRobot.enterUsername('Not Valid');
await loginRobot.enterEmail('Not Valid');
await loginRobot.tapLoginButton();
await loginRobot.checkInvalidCredentialsMessageIsShown();
});

testWidgets('authenticates a user with an username and password',
testWidgets('authenticates a user with an email and password',
(tester) async {
loginRobot = await _configure(tester);

await loginRobot.enterUsername('robot');
await loginRobot.enterPassword('1234');
await loginRobot.enterEmail('[email protected]');
await loginRobot.enterPassword('MyPass123');
await loginRobot.tapLoginButton();
});
});
Expand Down
27 changes: 13 additions & 14 deletions catalyst_voices/integration_test/scenarios/robots/login_robot.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:catalyst_voices/dummy/constants.dart';
import 'package:catalyst_voices/pages/login/login.dart';
import 'package:catalyst_voices/pages/widgets/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

final class LoginRobot {
Expand All @@ -9,36 +10,34 @@ final class LoginRobot {
});

Future<void> checkInvalidCredentialsMessageIsShown() async {
final loginErrorSnackbar = find.byKey(WidgetKeys.loginErrorSnackbar);
final loginErrorSnackbar = find.byKey(LoginForm.loginErrorSnackbarKey);
expect(loginErrorSnackbar, findsOneWidget);
await widgetTester.pump();
}

Future<void> enterPassword(String password) async {
final passwordTextController =
find.byKey(WidgetKeys.passwordTextController);
expect(passwordTextController, findsOneWidget);
await widgetTester.enterText(passwordTextController, password);
Future<void> enterEmail(String email) async {
final emailTextField = find.byKey(EmailInput.emailInputKey);
expect(emailTextField, findsOneWidget);
await widgetTester.enterText(emailTextField, email);
await widgetTester.pump();
}

Future<void> enterUsername(String username) async {
final usernameTextController =
find.byKey(WidgetKeys.usernameTextController);
expect(usernameTextController, findsOneWidget);
await widgetTester.enterText(usernameTextController, username);
Future<void> enterPassword(String password) async {
final passwordTextField = find.byKey(PasswordInput.passwordInputKey);
expect(passwordTextField, findsOneWidget);
await widgetTester.enterText(passwordTextField, password);
await widgetTester.pump();
}

Future<void> tapLoginButton() async {
final loginButton = find.byKey(WidgetKeys.loginButton);
final loginButton = find.byKey(LoginInButton.loginButtonKey);
expect(loginButton, findsOneWidget);
await widgetTester.tap(loginButton);
await widgetTester.pump();
}

void verifyLoginScreenIsShown() {
final loginScreen = find.byKey(WidgetKeys.loginScreen);
final loginScreen = find.byKey(LoginForm.loginFormKey);
expect(loginScreen, findsOneWidget);
}
}
24 changes: 2 additions & 22 deletions catalyst_voices/lib/app/view/app.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,2 @@
import 'package:catalyst_voices/dummy/dummy.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localized_locales/flutter_localized_locales.dart';

final class App extends StatelessWidget {
const App({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
restorationScopeId: 'rootVoices',
localizationsDelegates: const [
...VoicesLocalizations.localizationsDelegates,
LocaleNamesLocalizationsDelegate(),
],
supportedLocales: VoicesLocalizations.supportedLocales,
localeListResolutionCallback: basicLocaleListResolution,
home: isUserLoggedIn ? const HomeScreen() : const LoginPage(),
);
}
}
export 'app_content.dart';
export 'app_page.dart';
37 changes: 37 additions & 0 deletions catalyst_voices/lib/app/view/app_content.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:catalyst_voices/routes/routes.dart' show AppRouter;
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localized_locales/flutter_localized_locales.dart';

final class AppContent extends StatelessWidget {
const AppContent({super.key});

@override
Widget build(BuildContext context) {
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {},
child: MaterialApp.router(
restorationScopeId: 'rootVoices',
localizationsDelegates: const [
...VoicesLocalizations.localizationsDelegates,
LocaleNamesLocalizationsDelegate(),
],
supportedLocales: VoicesLocalizations.supportedLocales,
localeListResolutionCallback: basicLocaleListResolution,
routerConfig: AppRouter.init(
authenticationBloc: context.read<AuthenticationBloc>(),
),
title: 'Catalyst Voices',
theme: ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
type: BottomNavigationBarType.fixed,
),
),
),
);
}
}
59 changes: 59 additions & 0 deletions catalyst_voices/lib/app/view/app_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:catalyst_voices/app/view/app_content.dart';
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
import 'package:catalyst_voices_repositories/catalyst_voices_repositories.dart';
import 'package:catalyst_voices_services/catalyst_voices_services.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

final class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

final class _AppState extends State<App> {
late final AuthenticationRepository _authenticationRepository;
late final CredentialsStorageRepository _credentialsStorageRepository;

@override
Widget build(BuildContext context) {
return MultiRepositoryProvider(
providers: [
RepositoryProvider.value(
value: _authenticationRepository,
),
],
child: BlocProvider(
create: (_) => AuthenticationBloc(
authenticationRepository: _authenticationRepository,
),
child: const AppContent(),
),
);
}

@override
Future<void> dispose() async {
await _authenticationRepository.dispose();

super.dispose();
}

@override
void initState() {
super.initState();

_configureRepositories();
}

void _configureRepositories() {
_credentialsStorageRepository = CredentialsStorageRepository(
secureStorageService: SecureStorageService(),
);

_authenticationRepository = AuthenticationRepository(
credentialsStorageRepository: _credentialsStorageRepository,
);
}
}
5 changes: 5 additions & 0 deletions catalyst_voices/lib/configs/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import 'dart:developer';
import 'package:catalyst_voices/configs/app_bloc_observer.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:url_strategy/url_strategy.dart';

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

GoRouter.optionURLReflectsImperativeAPIs = true;
setPathUrlStrategy();

FlutterError.onError = (details) {
log(
details.exceptionAsString(),
Expand Down
12 changes: 0 additions & 12 deletions catalyst_voices/lib/dummy/constants.dart

This file was deleted.

8 changes: 0 additions & 8 deletions catalyst_voices/lib/dummy/dummy.dart

This file was deleted.

121 changes: 0 additions & 121 deletions catalyst_voices/lib/dummy/login_page.dart

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import 'package:catalyst_voices/dummy/dummy.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:flutter/material.dart';

final class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
final class HomePage extends StatelessWidget {
static const homePageKey = Key('HomePage');

const HomePage({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
key: WidgetKeys.homeScreen,
key: homePageKey,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
Expand Down
Loading

0 comments on commit 088ebcc

Please sign in to comment.