Skip to content

Commit d9b1657

Browse files
authored
Merge pull request #10 from Azerbaijan-Flutter-Users-Community/dev
Dev
2 parents e10b49f + d6a537b commit d9b1657

38 files changed

+626
-175
lines changed

coverage/lcov.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ DA:18,3
1818
DA:19,1
1919
LF:6
2020
LH:6
21-
end_of_record
21+
end_of_record

lib/app.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
4+
import 'package:flutter_localizations/flutter_localizations.dart';
5+
6+
import 'bloc/language/language_cubit.dart';
7+
import 'bloc/post/post_cubit.dart';
8+
import 'bloc/theme/theme_cubit.dart';
9+
import 'constants/app_themes.dart';
10+
import 'constants/supported_locales.dart';
11+
import 'data/repositories/post_repository.dart';
12+
import 'presentation/pages/posts/posts_page.dart';
13+
14+
class App extends StatelessWidget {
15+
@override
16+
Widget build(BuildContext context) {
17+
final themeMode = context.watch<ThemeCubit>().state;
18+
final locale = context.watch<LanguageCubit>().state;
19+
20+
return MaterialApp(
21+
title: 'Placeholder Title',
22+
debugShowCheckedModeBanner: false,
23+
themeMode: themeMode,
24+
theme: AppThemes.lightTheme,
25+
darkTheme: AppThemes.darkTheme,
26+
locale: locale,
27+
supportedLocales: supportedLocales,
28+
localizationsDelegates: [
29+
GlobalMaterialLocalizations.delegate,
30+
GlobalWidgetsLocalizations.delegate,
31+
GlobalCupertinoLocalizations.delegate,
32+
AppLocalizations.delegate,
33+
],
34+
home: Navigator(
35+
pages: [
36+
MaterialPage(
37+
child: RepositoryProvider(
38+
create: (_) => PostRepository(),
39+
child: BlocProvider(
40+
create: (ctx) => PostCubit(
41+
ctx.read<PostRepository>(),
42+
)..fetch(),
43+
child: PostsPage(),
44+
),
45+
),
46+
),
47+
],
48+
onPopPage: (route, result) {
49+
if (route.didPop(result)) {
50+
return true;
51+
}
52+
53+
return false;
54+
},
55+
),
56+
);
57+
}
58+
}

lib/bloc/data/data_cubit.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import 'dart:async';
2+
3+
import 'package:equatable/equatable.dart';
4+
import 'package:flutter_bloc/flutter_bloc.dart';
5+
import 'package:logging/logging.dart';
6+
7+
part 'data_state.dart';
8+
9+
abstract class DataCubit<T> extends Cubit<DataState<T>> {
10+
final _logger = Logger('DataCubit<$T>');
11+
12+
DataCubit() : super(DataState<T>());
13+
14+
bool get emitInProgress => true;
15+
16+
FutureOr<T> loadData();
17+
18+
void fetch() async {
19+
emit(state.inProgress());
20+
21+
try {
22+
final data = await loadData();
23+
24+
if (data != null) {
25+
_logger.fine('$data');
26+
emit(state.success(data));
27+
} else {
28+
_logger.warning('data is null!');
29+
emit(state.failure());
30+
}
31+
} catch (e, s) {
32+
_logger.severe('$e => $s');
33+
}
34+
}
35+
}

lib/bloc/data/data_state.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
part of 'data_cubit.dart';
2+
3+
class DataState<T> extends Equatable {
4+
DataState({
5+
this.isInProgress = false,
6+
this.isFailure = false,
7+
this.isSuccess = false,
8+
this.data,
9+
});
10+
11+
final bool isInProgress;
12+
final bool isFailure;
13+
final bool isSuccess;
14+
final T? data;
15+
16+
DataState<T> inProgress() => _copyWith(isInProgress: true);
17+
18+
DataState<T> failure() => _copyWith(isFailure: true);
19+
20+
DataState<T> success(T data) => _copyWith(isSuccess: true, data: data);
21+
22+
DataState<T> _copyWith({
23+
final bool? isInProgress,
24+
final bool? isFailure,
25+
final bool? isSuccess,
26+
final T? data,
27+
}) =>
28+
DataState(
29+
isInProgress: isInProgress ?? false,
30+
isFailure: isFailure ?? false,
31+
isSuccess: isSuccess ?? false,
32+
data: data,
33+
);
34+
35+
@override
36+
List<Object?> get props => [isInProgress, isFailure, isSuccess, data];
37+
}

lib/bloc/language/language_cubit.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
33

4-
import '../../contractors/impl_preferences_service.dart';
4+
import '../../contractors/base_preferences_service.dart';
55

66
class LanguageCubit extends Cubit<Locale> {
77
LanguageCubit(this.preferencesService) : super(Locale('en', 'US')) {
88
final locale = preferencesService.locale;
99
emit(locale ?? Locale('en', 'US'));
1010
}
1111

12-
final ImplPreferencesService preferencesService;
12+
final BasePreferencesService preferencesService;
1313

1414
void changeLocale(Locale locale) async {
1515
if (state == locale) {

lib/bloc/post/post_cubit.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'dart:async';
2+
3+
import '../../contractors/base_post_repository.dart';
4+
import '../../data/models/post.dart';
5+
import '../data/data_cubit.dart';
6+
7+
export '../data/data_cubit.dart';
8+
export '../../data/models/post.dart';
9+
10+
class PostCubit extends DataCubit<List<Post>> {
11+
PostCubit(this.postRepository);
12+
13+
final BasePostRepository postRepository;
14+
15+
@override
16+
FutureOr<List<Post>> loadData() => postRepository.getPosts();
17+
}

lib/bloc/theme/theme_cubit.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
33

4-
import '../../contractors/impl_preferences_service.dart';
4+
import '../../contractors/base_preferences_service.dart';
55

66
class ThemeCubit extends Cubit<ThemeMode> {
77
ThemeCubit(this.preferencesService) : super(ThemeMode.system) {
88
emit(preferencesService.themeMode ?? ThemeMode.system);
99
}
1010

11-
ImplPreferencesService preferencesService;
11+
BasePreferencesService preferencesService;
1212

1313
void changeTheme(ThemeMode themeMode) async {
1414
if (state == themeMode) {

lib/config/init.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import 'package:dio/dio.dart';
12
import 'package:flutter/material.dart';
23
import 'package:flutter_bloc/flutter_bloc.dart';
34

45
import '../bloc/app_bloc_observer.dart';
6+
import '../data/data_providers/post_data_provider.dart';
7+
import '../locator.dart';
58
import '../services/logging_service.dart';
69
import '../services/preferences_service.dart';
710
import 'config.dart';
@@ -18,4 +21,12 @@ Future<void> init() async {
1821
}
1922

2023
await PreferencesService.init();
24+
await _initDataProviders();
25+
}
26+
27+
Future<void> _initDataProviders() async {
28+
final locator = Locator.instance;
29+
final dio = Dio();
30+
31+
locator.register<PostDataProvider>(PostDataProvider(dio));
2132
}

lib/presentation/values/app_colors.dart renamed to lib/constants/app_colors.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import 'package:flutter/material.dart';
33
abstract class AppColors {
44
AppColors._();
55

6-
static const primaryColor = Colors.orange;
6+
static const primaryColor = Colors.deepPurple;
77
static const darkPrimaryColor = Colors.black;
88

9-
static const primaryColorLight = Colors.orangeAccent;
9+
static const primaryColorLight = Colors.deepPurple;
1010
static const darkPrimaryColorLight = Colors.black45;
1111

12-
static const primaryColorDark = Colors.deepOrange;
12+
static const primaryColorDark = Colors.deepPurple;
1313

14-
static const scaffoldColor = Colors.white;
14+
static const scaffoldColor = Colors.grey;
1515
static const darkScaffoldColor = Colors.black;
1616
}
File renamed without changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import '../data/models/post.dart';
2+
3+
abstract class BasePostRepository {
4+
Future<List<Post>> getPosts();
5+
}

lib/contractors/impl_preferences_service.dart renamed to lib/contractors/base_preferences_service.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'package:flutter/material.dart';
22

3-
abstract class ImplPreferencesService {
3+
abstract class BasePreferencesService {
44
Future<void> changeThemeMode(ThemeMode themeMode);
55
ThemeMode? get themeMode;
66
Future<void> changeLocale(Locale locale);

lib/data/data_providers/.gitkeep

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import 'package:dio/dio.dart';
2+
import 'package:retrofit/retrofit.dart';
3+
4+
import '../models/post.dart';
5+
6+
part 'post_data_provider.g.dart';
7+
8+
@RestApi(baseUrl: 'https://jsonplaceholder.typicode.com/')
9+
abstract class PostDataProvider {
10+
factory PostDataProvider(Dio dio, {String baseUrl}) = _PostDataProvider;
11+
12+
@GET('/posts')
13+
Future<List<Post>> getPosts();
14+
}

lib/data/data_providers/post_data_provider.g.dart

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/data/models/.gitkeep

Whitespace-only changes.

lib/data/models/post.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Post {
2+
final int userId;
3+
final int id;
4+
final String title;
5+
final String body;
6+
7+
Post({
8+
required this.userId,
9+
required this.id,
10+
required this.title,
11+
required this.body,
12+
});
13+
14+
factory Post.fromJson(Map<String, dynamic> json) {
15+
return Post(
16+
userId: json['userId'],
17+
id: json['id'],
18+
title: json['title'],
19+
body: json['body'],
20+
);
21+
}
22+
}

lib/data/repositories/.gitkeep

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'package:architecture_example/data/models/post.dart';
2+
3+
import '../../contractors/base_post_repository.dart';
4+
import '../../locator.dart';
5+
import '../data_providers/post_data_provider.dart';
6+
7+
class PostRepository implements BasePostRepository {
8+
final _postDataProvider = Locator.instance.get<PostDataProvider>();
9+
10+
@override
11+
Future<List<Post>> getPosts() => _postDataProvider.getPosts();
12+
}

lib/exceptions/.gitkeep

Whitespace-only changes.
File renamed without changes.

lib/locator.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
typedef AsyncRegister<T> = Future<T> Function();
2+
3+
class Locator {
4+
Locator._();
5+
6+
static _Locator? _instance = _Locator();
7+
8+
static _Locator get instance => _instance!;
9+
10+
T get<T>() => _instance!.get<T>();
11+
12+
void register<T>(T instance) => _instance!.register<T>(instance);
13+
14+
Future<void> registerAsync<T>(AsyncRegister<T> asyncBuilder) =>
15+
_instance!.registerAsync<T>(asyncBuilder);
16+
17+
void close() {
18+
_instance!.close();
19+
_instance = null;
20+
}
21+
}
22+
23+
class _Locator implements Locator {
24+
final _services = <Type, dynamic>{};
25+
26+
T get<T>() {
27+
if (_services.containsKey(T)) return _services[T]!;
28+
throw LocatorNotFoundException();
29+
}
30+
31+
void register<T>(T instance) {
32+
_services.putIfAbsent(T, () => instance);
33+
}
34+
35+
Future<void> registerAsync<T>(AsyncRegister<T> asyncBuilder) async {
36+
final instance = await asyncBuilder.call();
37+
_services.putIfAbsent(T, () => instance);
38+
}
39+
40+
@override
41+
void close() {
42+
_services.clear();
43+
}
44+
}
45+
46+
class LocatorNotFoundException implements Exception {}

0 commit comments

Comments
 (0)