Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Max Khobotin committed May 30, 2023
1 parent aeb070a commit f8dbd19
Show file tree
Hide file tree
Showing 114 changed files with 6,415 additions and 36 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
mason-lock.json
mason.yaml
.mason/bricks.json
.vscode/launch.json
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
8 changes: 8 additions & 0 deletions build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
targets:
$default:
builders:
json_serializable:
options:
field_rename: snake
checked: true
explicit_to_json: true
5 changes: 5 additions & 0 deletions l10n.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
arb-dir: lib/l10n
template-arb-file: l10n_en.arb
output-localization-file: l10n.dart
output-class: L10n
nullable-getter: false
31 changes: 31 additions & 0 deletions lib/app.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:breed_repository/breed_repository.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'common/nav/router.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

class BreedApp extends StatelessWidget {
const BreedApp({required BreedRepository breedRepository, super.key}) : _breedRepository = breedRepository;

final BreedRepository _breedRepository;

@override
Widget build(BuildContext context) {
return RepositoryProvider.value(
value: _breedRepository,
child: const BreedAppView(),
);
}
}

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

@override
Widget build(BuildContext context) => MaterialApp.router(
routerConfig: AppRouter(),
localizationsDelegates: L10n.localizationsDelegates,
supportedLocales: L10n.supportedLocales,
);
}
34 changes: 34 additions & 0 deletions lib/common/bloc_observer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'dart:developer';

import 'package:bloc/bloc.dart';

class AppBlocObserver extends BlocObserver {
const AppBlocObserver();

@override
void onEvent(Bloc<dynamic, dynamic> bloc, Object? event) {
super.onEvent(bloc, event);
log('onEvent $event');
}

@override
void onChange(BlocBase<dynamic> bloc, Change<dynamic> change) {
super.onChange(bloc, change);
log('onChange $change');
}

@override
void onTransition(
Bloc<dynamic, dynamic> bloc,
Transition<dynamic, dynamic> transition,
) {
super.onTransition(bloc, transition);
log('onTransition $transition');
}

@override
void onError(BlocBase<dynamic> bloc, Object error, StackTrace stackTrace) {
super.onError(bloc, error, stackTrace);
log('onError $error');
}
}
55 changes: 55 additions & 0 deletions lib/common/nav/router.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:go_router/go_router.dart';

import '../../features/breed/view/breed_details_screen.dart';
import '../../features/breed/view/breed_list_screen.dart';
import '../../features/favorites/view/favorites.dart';
import '../../features/search/view/search.dart';
import '../view/scaffold_with_nav_bar.dart';

// GoRouter configuration
class AppRouter extends GoRouter {
AppRouter()
: super(
initialLocation: '/breedList',
debugLogDiagnostics: true,
routes: [
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) {
return ScaffoldWithNavBar(navigationShell: navigationShell);
},
branches: [
StatefulShellBranch(
routes: [
GoRoute(
path: '/breedList',
builder: (context, state) => const BreedListScreen(),
routes: [
GoRoute(
path: 'breedDetails',
builder: (context, state) => const BreedDetailsScreen(),
)
],
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/search',
builder: (context, state) => const SearchScreen(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/favorites',
builder: (context, state) => const FavoritesScreen(),
),
],
),
],
),
],
);
}
39 changes: 39 additions & 0 deletions lib/common/view/scaffold_with_nav_bar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:go_router/go_router.dart';

class ScaffoldWithNavBar extends StatelessWidget {
final StatefulNavigationShell navigationShell;

const ScaffoldWithNavBar({super.key, required this.navigationShell});

@override
Widget build(BuildContext context) {
return Scaffold(
body: navigationShell,
bottomNavigationBar: BottomNavigationBar(
items: [
// Here, the items of BottomNavigationBar are hard coded. In a real
// world scenario, the items would most likely be generated from the
// branches of the shell route, which can be fetched using
// `navigationShell.route.branches`.
BottomNavigationBarItem(
icon: const FaIcon(FontAwesomeIcons.dog),
label: L10n.of(context).breeds,
),
BottomNavigationBarItem(
icon: const FaIcon(FontAwesomeIcons.magnifyingGlass),
label: L10n.of(context).search,
),
BottomNavigationBarItem(
icon: const FaIcon(FontAwesomeIcons.solidStar),
label: L10n.of(context).favorites,
),
],
currentIndex: navigationShell.currentIndex,
onTap: (index) => navigationShell.goBranch(index, initialLocation: index == navigationShell.currentIndex),
),
);
}
}
38 changes: 38 additions & 0 deletions lib/features/breed/cubit/breed_list_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'package:bloc/bloc.dart';
import 'package:breed_repository/breed_repository.dart' show BreedRepository;
import 'package:freezed_annotation/freezed_annotation.dart';

import '../models/breed/breed.dart';

part 'breed_list_state.dart';
part 'breed_list_cubit.freezed.dart';

class BreedListCubit extends Cubit<BreedListState> {
BreedListCubit(this._breedRepository) : super(const BreedListState());

final BreedRepository _breedRepository;

Future<void> fetchInitialPage() => _fetchBreedList(page: 0);

Future<void> fetchNextPage() async {
if (state.status == BreedListStatus.loading) return;
return _fetchBreedList(page: state.page + 1);
}

Future<void> _fetchBreedList({int page = 0}) async {
if (page < 0) return;

emit(state.copyWith(status: BreedListStatus.loading));

try {
final breeds = (await _breedRepository.getBreeds(page: page)).map((b) => Breed.fromRepo(b)).toList();
emit(state.copyWith(
status: BreedListStatus.success,
breeds: state.breeds + breeds,
page: page,
));
} on Exception {
emit(state.copyWith(status: BreedListStatus.failure));
}
}
}
Loading

0 comments on commit f8dbd19

Please sign in to comment.