From 4d9f805aa8216745d1816d2346ade2a308eacadc Mon Sep 17 00:00:00 2001 From: yusubx Date: Sat, 1 May 2021 00:01:02 +0400 Subject: [PATCH 1/8] changed --- lib/constants/routes.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/constants/routes.dart b/lib/constants/routes.dart index 41fd715..b526894 100644 --- a/lib/constants/routes.dart +++ b/lib/constants/routes.dart @@ -1,3 +1,7 @@ class Routes { Routes._(); + + static const String home = '/'; + static const String signIn = '/signIn'; + static const String settings = '/settings'; } From 120668362ac63e3e6822c7364e7c3f8c046c36d8 Mon Sep 17 00:00:00 2001 From: yusubx Date: Sat, 1 May 2021 00:07:17 +0400 Subject: [PATCH 2/8] analyze error fixed --- lib/presentation/pages/posts/posts_page.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/presentation/pages/posts/posts_page.dart b/lib/presentation/pages/posts/posts_page.dart index 0d779a8..998a80e 100644 --- a/lib/presentation/pages/posts/posts_page.dart +++ b/lib/presentation/pages/posts/posts_page.dart @@ -1,4 +1,3 @@ -import 'package:architecture_example/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; From d232632490e85cbce0c9194ca4c74aae3eac242e Mon Sep 17 00:00:00 2001 From: yusubx Date: Sun, 2 May 2021 11:05:57 +0400 Subject: [PATCH 3/8] settings initialized --- lib/app.dart | 2 ++ .../pages/posts/widgets/more_menu.dart | 14 ++++++++++ .../pages/posts/widgets/post_item.dart | 2 +- .../pages/settings/settings_page.dart | 10 +++++++ lib/presentation/router/.gitkeep | 0 .../router/router_controller.dart | 28 +++++++++++++++++++ 6 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 lib/presentation/pages/posts/widgets/more_menu.dart create mode 100644 lib/presentation/pages/settings/settings_page.dart delete mode 100644 lib/presentation/router/.gitkeep create mode 100644 lib/presentation/router/router_controller.dart diff --git a/lib/app.dart b/lib/app.dart index 138b64b..c8f427e 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -10,6 +10,7 @@ import 'constants/app_themes.dart'; import 'constants/supported_locales.dart'; import 'data/repositories/post_repository.dart'; import 'presentation/pages/posts/posts_page.dart'; +import 'presentation/router/router_controller.dart'; class App extends StatelessWidget { @override @@ -53,6 +54,7 @@ class App extends StatelessWidget { return false; }, ), + onGenerateRoute: RouteController.onGenerateRoute, ); } } diff --git a/lib/presentation/pages/posts/widgets/more_menu.dart b/lib/presentation/pages/posts/widgets/more_menu.dart new file mode 100644 index 0000000..7c3dfe2 --- /dev/null +++ b/lib/presentation/pages/posts/widgets/more_menu.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class MoreMenu extends StatelessWidget { + const MoreMenu({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return PopupMenuButton( + itemBuilder: (_) { + return >[]; + }, + ); + } +} diff --git a/lib/presentation/pages/posts/widgets/post_item.dart b/lib/presentation/pages/posts/widgets/post_item.dart index a9ab71a..5663556 100644 --- a/lib/presentation/pages/posts/widgets/post_item.dart +++ b/lib/presentation/pages/posts/widgets/post_item.dart @@ -35,7 +35,7 @@ class PostItem extends StatelessWidget { ), decoration: BoxDecoration( color: Theme.of(context).cardColor, - borderRadius: BorderRadius.circular(8.0), + borderRadius: BorderRadius.circular(4.0), boxShadow: [ BoxShadow( color: Theme.of(context).primaryColor.withOpacity(0.2), diff --git a/lib/presentation/pages/settings/settings_page.dart b/lib/presentation/pages/settings/settings_page.dart new file mode 100644 index 0000000..745786c --- /dev/null +++ b/lib/presentation/pages/settings/settings_page.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class SettingsPage extends StatelessWidget { + const SettingsPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold(); + } +} diff --git a/lib/presentation/router/.gitkeep b/lib/presentation/router/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/presentation/router/router_controller.dart b/lib/presentation/router/router_controller.dart new file mode 100644 index 0000000..9c2ba38 --- /dev/null +++ b/lib/presentation/router/router_controller.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +import '../../constants/routes.dart'; +import '../pages/posts/posts_page.dart'; +import '../pages/settings/settings_page.dart'; + +class RouteController { + RouteController._(); + + static Route? onGenerateRoute(RouteSettings routeSettings) { + switch (routeSettings.name) { + case Routes.signIn: + break; + case Routes.home: + return MaterialPageRoute( + builder: (_) => PostsPage(), + ); + case Routes.settings: + return MaterialPageRoute( + builder: (_) => SettingsPage(), + ); + default: + throw UnimplementedError( + 'Route: ${routeSettings.name} is not defined!', + ); + } + } +} From a24f16aa3dadbfd66310dbfb671ff4e655ea1c75 Mon Sep 17 00:00:00 2001 From: yusubx Date: Sun, 2 May 2021 13:20:10 +0400 Subject: [PATCH 4/8] navigator 2.0 applied --- lib/app.dart | 33 ++------- lib/bloc/app_state/app_state.dart | 3 + lib/bloc/post/post_cubit.dart | 14 +++- lib/constants/routes.dart | 4 +- lib/data/models/post.dart | 2 +- lib/presentation/pages/posts/posts_page.dart | 8 +-- .../pages/posts/widgets/action_button.dart | 2 +- .../pages/posts/widgets/more_menu.dart | 21 +++++- .../router/app_route_delegate.dart | 69 +++++++++++++++++++ .../router/app_route_information_parser.dart | 9 +++ .../router/router_controller.dart | 28 -------- 11 files changed, 126 insertions(+), 67 deletions(-) create mode 100644 lib/bloc/app_state/app_state.dart create mode 100644 lib/presentation/router/app_route_delegate.dart create mode 100644 lib/presentation/router/app_route_information_parser.dart delete mode 100644 lib/presentation/router/router_controller.dart diff --git a/lib/app.dart b/lib/app.dart index c8f427e..a7c4744 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,16 +1,14 @@ +import 'package:architecture_example/presentation/router/app_route_delegate.dart'; +import 'package:architecture_example/presentation/router/app_route_information_parser.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'bloc/language/language_cubit.dart'; -import 'bloc/post/post_cubit.dart'; import 'bloc/theme/theme_cubit.dart'; import 'constants/app_themes.dart'; import 'constants/supported_locales.dart'; -import 'data/repositories/post_repository.dart'; -import 'presentation/pages/posts/posts_page.dart'; -import 'presentation/router/router_controller.dart'; class App extends StatelessWidget { @override @@ -18,7 +16,9 @@ class App extends StatelessWidget { final themeMode = context.watch().state; final locale = context.watch().state; - return MaterialApp( + return MaterialApp.router( + routerDelegate: AppRouteDelegate(), + routeInformationParser: AppRouteInformationParser(), title: 'Placeholder Title', debugShowCheckedModeBanner: false, themeMode: themeMode, @@ -32,29 +32,6 @@ class App extends StatelessWidget { GlobalCupertinoLocalizations.delegate, AppLocalizations.delegate, ], - home: Navigator( - pages: [ - MaterialPage( - child: RepositoryProvider( - create: (_) => PostRepository(), - child: BlocProvider( - create: (ctx) => PostCubit( - ctx.read(), - )..fetch(), - child: PostsPage(), - ), - ), - ), - ], - onPopPage: (route, result) { - if (route.didPop(result)) { - return true; - } - - return false; - }, - ), - onGenerateRoute: RouteController.onGenerateRoute, ); } } diff --git a/lib/bloc/app_state/app_state.dart b/lib/bloc/app_state/app_state.dart new file mode 100644 index 0000000..4d3c9d7 --- /dev/null +++ b/lib/bloc/app_state/app_state.dart @@ -0,0 +1,3 @@ +import 'package:flutter/cupertino.dart'; + +class AppState extends ChangeNotifier {} diff --git a/lib/bloc/post/post_cubit.dart b/lib/bloc/post/post_cubit.dart index 000633f..e0e6ee3 100644 --- a/lib/bloc/post/post_cubit.dart +++ b/lib/bloc/post/post_cubit.dart @@ -13,5 +13,17 @@ class PostCubit extends DataCubit> { final BasePostRepository postRepository; @override - FutureOr> loadData() => postRepository.getPosts(); + FutureOr> loadData() async { + final posts = await postRepository.getPosts(); + + return posts + .map( + (post) => post + ..body = post.body.replaceAll( + '\n', + ' ', + ), + ) + .toList(); + } } diff --git a/lib/constants/routes.dart b/lib/constants/routes.dart index b526894..f7cc6a3 100644 --- a/lib/constants/routes.dart +++ b/lib/constants/routes.dart @@ -1,7 +1,9 @@ class Routes { Routes._(); - static const String home = '/'; + static const String splash = '/splash'; static const String signIn = '/signIn'; + static const String posts = '/posts'; static const String settings = '/settings'; + static const String about = '/about'; } diff --git a/lib/data/models/post.dart b/lib/data/models/post.dart index e192354..f46d9e0 100644 --- a/lib/data/models/post.dart +++ b/lib/data/models/post.dart @@ -2,7 +2,7 @@ class Post { final int userId; final int id; final String title; - final String body; + String body; Post({ required this.userId, diff --git a/lib/presentation/pages/posts/posts_page.dart b/lib/presentation/pages/posts/posts_page.dart index 998a80e..d39a1d7 100644 --- a/lib/presentation/pages/posts/posts_page.dart +++ b/lib/presentation/pages/posts/posts_page.dart @@ -6,6 +6,7 @@ import '../../../bloc/post/post_cubit.dart'; import '../../../utils/extensions/waitable_cubit_ext.dart'; import 'widgets/home_bottom_bar.dart'; import 'widgets/post_item.dart'; +import 'widgets/more_menu.dart'; class PostsPage extends StatelessWidget { @override @@ -25,12 +26,7 @@ class PostsPage extends StatelessWidget { SliverAppBar( title: Text('Posts'), backgroundColor: mainColor, - actions: [ - IconButton( - icon: Icon(Icons.more_vert), - onPressed: () {}, - ), - ], + actions: [MoreMenu()], ), SliverFillRemaining( child: RefreshIndicator( diff --git a/lib/presentation/pages/posts/widgets/action_button.dart b/lib/presentation/pages/posts/widgets/action_button.dart index 4b50777..02ef979 100644 --- a/lib/presentation/pages/posts/widgets/action_button.dart +++ b/lib/presentation/pages/posts/widgets/action_button.dart @@ -14,7 +14,7 @@ class ActionButton extends StatelessWidget { Widget build(BuildContext context) { return Expanded( child: Material( - color: Theme.of(context).cardColor, + color: Theme.of(context).cardColor.withOpacity(0.0), child: InkWell( onTap: onTap, child: Padding( diff --git a/lib/presentation/pages/posts/widgets/more_menu.dart b/lib/presentation/pages/posts/widgets/more_menu.dart index 7c3dfe2..b153d01 100644 --- a/lib/presentation/pages/posts/widgets/more_menu.dart +++ b/lib/presentation/pages/posts/widgets/more_menu.dart @@ -1,13 +1,32 @@ import 'package:flutter/material.dart'; +import '../../../../constants/routes.dart'; + class MoreMenu extends StatelessWidget { const MoreMenu({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return PopupMenuButton( + color: Theme.of(context).primaryColor, + onSelected: (String value) { + print('onSelected: $value'); + if (value == 'Settings') { + Navigator.of(context).pushNamed(Routes.settings); + } else if (value == 'About') {} + }, itemBuilder: (_) { - return >[]; + return >[ + PopupMenuItem( + value: 'Settings', + child: Text('Settings'), + ), + PopupMenuDivider(), + PopupMenuItem( + value: 'About', + child: Text('About'), + ), + ]; }, ); } diff --git a/lib/presentation/router/app_route_delegate.dart b/lib/presentation/router/app_route_delegate.dart new file mode 100644 index 0000000..0192bb0 --- /dev/null +++ b/lib/presentation/router/app_route_delegate.dart @@ -0,0 +1,69 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../bloc/post/post_cubit.dart'; +import '../../constants/routes.dart'; +import '../../data/repositories/post_repository.dart'; +import '../pages/posts/posts_page.dart'; +import '../pages/settings/settings_page.dart'; + +class AppRouteDelegate extends RouterDelegate + with ChangeNotifier, PopNavigatorRouterDelegateMixin { + final _navigatorKey = GlobalKey(); + + @override + GlobalKey? get navigatorKey => _navigatorKey; + + final _pages = [ + MaterialPage( + child: RepositoryProvider( + create: (_) => PostRepository(), + child: BlocProvider( + create: (ctx) => PostCubit( + ctx.read(), + )..fetch(), + child: PostsPage(), + ), + ), + ), + ]; + + @override + Widget build(BuildContext context) { + return Navigator( + key: navigatorKey, + pages: _pages, + onPopPage: (route, result) { + if (route.didPop(result)) { + return true; + } + + return false; + }, + onGenerateRoute: (RouteSettings routeSettings) { + switch (routeSettings.name) { + case Routes.signIn: + break; + case Routes.posts: + return MaterialPageRoute( + builder: (_) => PostsPage(), + ); + case Routes.settings: + return MaterialPageRoute( + builder: (_) => SettingsPage(), + ); + default: + throw UnimplementedError( + 'Route: ${routeSettings.name} is not defined!', + ); + } + }, + ); + } + + @override + Future setNewRoutePath(String configuration) { + return SynchronousFuture(configuration); + } +} diff --git a/lib/presentation/router/app_route_information_parser.dart b/lib/presentation/router/app_route_information_parser.dart new file mode 100644 index 0000000..9b38dd1 --- /dev/null +++ b/lib/presentation/router/app_route_information_parser.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + +class AppRouteInformationParser extends RouteInformationParser { + @override + Future parseRouteInformation( + RouteInformation routeInformation) async { + return routeInformation.location!; + } +} diff --git a/lib/presentation/router/router_controller.dart b/lib/presentation/router/router_controller.dart deleted file mode 100644 index 9c2ba38..0000000 --- a/lib/presentation/router/router_controller.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../constants/routes.dart'; -import '../pages/posts/posts_page.dart'; -import '../pages/settings/settings_page.dart'; - -class RouteController { - RouteController._(); - - static Route? onGenerateRoute(RouteSettings routeSettings) { - switch (routeSettings.name) { - case Routes.signIn: - break; - case Routes.home: - return MaterialPageRoute( - builder: (_) => PostsPage(), - ); - case Routes.settings: - return MaterialPageRoute( - builder: (_) => SettingsPage(), - ); - default: - throw UnimplementedError( - 'Route: ${routeSettings.name} is not defined!', - ); - } - } -} From 01ecfefe4b44ce7947b76ebba54a0b0a6039575a Mon Sep 17 00:00:00 2001 From: yusubx Date: Mon, 3 May 2021 16:58:22 +0400 Subject: [PATCH 5/8] settings page completed --- lib/app.dart | 10 +-- lib/bloc/theme/theme_cubit.dart | 1 + lib/l10n/app_az.arb | 3 +- lib/l10n/app_en.arb | 8 +-- lib/presentation/pages/posts/posts_page.dart | 9 ++- .../pages/posts/widgets/home_bottom_bar.dart | 5 +- .../pages/posts/widgets/more_menu.dart | 4 +- .../pages/settings/settings_page.dart | 56 ++++++++++++++- .../settings/widgets/language_selection.dart | 39 +++++++++++ .../settings/widgets/theme_selection.dart | 42 +++++++++++ .../pages/splash/splash_page.dart | 47 +++++++++++++ .../router/app_route_delegate.dart | 69 ------------------- .../router/app_route_information_parser.dart | 9 --- lib/presentation/router/route_controller.dart | 35 ++++++++++ 14 files changed, 240 insertions(+), 97 deletions(-) create mode 100644 lib/presentation/pages/settings/widgets/language_selection.dart create mode 100644 lib/presentation/pages/settings/widgets/theme_selection.dart create mode 100644 lib/presentation/pages/splash/splash_page.dart delete mode 100644 lib/presentation/router/app_route_delegate.dart delete mode 100644 lib/presentation/router/app_route_information_parser.dart create mode 100644 lib/presentation/router/route_controller.dart diff --git a/lib/app.dart b/lib/app.dart index a7c4744..ff9e253 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,5 +1,3 @@ -import 'package:architecture_example/presentation/router/app_route_delegate.dart'; -import 'package:architecture_example/presentation/router/app_route_information_parser.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -9,6 +7,8 @@ import 'bloc/language/language_cubit.dart'; import 'bloc/theme/theme_cubit.dart'; import 'constants/app_themes.dart'; import 'constants/supported_locales.dart'; +import 'presentation/pages/splash/splash_page.dart'; +import 'presentation/router/route_controller.dart'; class App extends StatelessWidget { @override @@ -16,9 +16,7 @@ class App extends StatelessWidget { final themeMode = context.watch().state; final locale = context.watch().state; - return MaterialApp.router( - routerDelegate: AppRouteDelegate(), - routeInformationParser: AppRouteInformationParser(), + return MaterialApp( title: 'Placeholder Title', debugShowCheckedModeBanner: false, themeMode: themeMode, @@ -26,12 +24,14 @@ class App extends StatelessWidget { darkTheme: AppThemes.darkTheme, locale: locale, supportedLocales: supportedLocales, + home: SplashPage(), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, AppLocalizations.delegate, ], + onGenerateRoute: RouteController.onGenerateRoute, ); } } diff --git a/lib/bloc/theme/theme_cubit.dart b/lib/bloc/theme/theme_cubit.dart index f5bffc5..ca8888d 100644 --- a/lib/bloc/theme/theme_cubit.dart +++ b/lib/bloc/theme/theme_cubit.dart @@ -11,6 +11,7 @@ class ThemeCubit extends Cubit { BasePreferencesService preferencesService; void changeTheme(ThemeMode themeMode) async { + print('themeMode: $themeMode'); if (state == themeMode) { return; } diff --git a/lib/l10n/app_az.arb b/lib/l10n/app_az.arb index d5bd6b3..2374603 100644 --- a/lib/l10n/app_az.arb +++ b/lib/l10n/app_az.arb @@ -1,3 +1,4 @@ { - "hi": "Salam!" + "posts": "Postlar", + "profile": "Profil" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 87a1e1f..8989f6a 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,6 +1,6 @@ { - "hi": "Hi!", - "@hi": { - "description": "The conventional newborn programmer greeting" - } + "posts": "Posts", + "@posts": {}, + "profile": "Profile", + "@profile": {} } \ No newline at end of file diff --git a/lib/presentation/pages/posts/posts_page.dart b/lib/presentation/pages/posts/posts_page.dart index d39a1d7..522dcb9 100644 --- a/lib/presentation/pages/posts/posts_page.dart +++ b/lib/presentation/pages/posts/posts_page.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../bloc/post/post_cubit.dart'; import '../../../utils/extensions/waitable_cubit_ext.dart'; import 'widgets/home_bottom_bar.dart'; -import 'widgets/post_item.dart'; import 'widgets/more_menu.dart'; +import 'widgets/post_item.dart'; class PostsPage extends StatelessWidget { @override @@ -24,7 +25,9 @@ class PostsPage extends StatelessWidget { child: CustomScrollView( slivers: [ SliverAppBar( - title: Text('Posts'), + title: Text( + AppLocalizations.of(context)!.posts, + ), backgroundColor: mainColor, actions: [MoreMenu()], ), @@ -63,7 +66,7 @@ class PostsPage extends StatelessWidget { return Text('Error occurred!'); } - return Expanded( + return Center( child: Container( color: Colors.white, ), diff --git a/lib/presentation/pages/posts/widgets/home_bottom_bar.dart b/lib/presentation/pages/posts/widgets/home_bottom_bar.dart index dbb1435..e7e3928 100644 --- a/lib/presentation/pages/posts/widgets/home_bottom_bar.dart +++ b/lib/presentation/pages/posts/widgets/home_bottom_bar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class HomeBottomBar extends StatefulWidget { @override @@ -19,11 +20,11 @@ class _HomeBottomBarState extends State { }, items: [ BottomNavigationBarItem( - label: 'Posts', + label: AppLocalizations.of(context)!.posts, icon: Icon(Icons.list_alt), ), BottomNavigationBarItem( - label: 'Profile', + label: AppLocalizations.of(context)!.profile, icon: Icon(Icons.account_circle_rounded), ), ], diff --git a/lib/presentation/pages/posts/widgets/more_menu.dart b/lib/presentation/pages/posts/widgets/more_menu.dart index b153d01..1566df1 100644 --- a/lib/presentation/pages/posts/widgets/more_menu.dart +++ b/lib/presentation/pages/posts/widgets/more_menu.dart @@ -8,7 +8,7 @@ class MoreMenu extends StatelessWidget { @override Widget build(BuildContext context) { return PopupMenuButton( - color: Theme.of(context).primaryColor, + color: Theme.of(context).cardColor, onSelected: (String value) { print('onSelected: $value'); if (value == 'Settings') { @@ -21,7 +21,7 @@ class MoreMenu extends StatelessWidget { value: 'Settings', child: Text('Settings'), ), - PopupMenuDivider(), + PopupMenuDivider(), PopupMenuItem( value: 'About', child: Text('About'), diff --git a/lib/presentation/pages/settings/settings_page.dart b/lib/presentation/pages/settings/settings_page.dart index 745786c..1fbc045 100644 --- a/lib/presentation/pages/settings/settings_page.dart +++ b/lib/presentation/pages/settings/settings_page.dart @@ -1,10 +1,62 @@ import 'package:flutter/material.dart'; +import 'widgets/theme_selection.dart'; +import 'widgets/language_selection.dart'; -class SettingsPage extends StatelessWidget { +class SettingsPage extends StatefulWidget { const SettingsPage({Key? key}) : super(key: key); + @override + _SettingsPageState createState() => _SettingsPageState(); +} + +class _SettingsPageState extends State { + bool isLangExpanded = false; + bool isThemeExpanded = false; + @override Widget build(BuildContext context) { - return Scaffold(); + return Scaffold( + appBar: AppBar( + title: Text('Settings'), + ), + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.all(8.0), + child: Container( + child: ExpansionPanelList( + expansionCallback: (int index, bool isExpanded) { + setState(() { + if (index == 0) { + isLangExpanded = !isExpanded; + } else if (index == 1) { + isThemeExpanded = !isExpanded; + } + }); + }, + children: [ + ExpansionPanel( + isExpanded: isLangExpanded, + headerBuilder: (_, isExpanded) { + return ListTile( + title: Text('Language'), + ); + }, + body: LanguageSelection(), + ), + ExpansionPanel( + isExpanded: isThemeExpanded, + headerBuilder: (_, isExpanded) { + return ListTile( + title: Text('Theme'), + ); + }, + body: ThemeSelection(), + ), + ], + ), + ), + ), + ), + ); } } diff --git a/lib/presentation/pages/settings/widgets/language_selection.dart b/lib/presentation/pages/settings/widgets/language_selection.dart new file mode 100644 index 0000000..ae29085 --- /dev/null +++ b/lib/presentation/pages/settings/widgets/language_selection.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../../bloc/language/language_cubit.dart'; + +class LanguageSelection extends StatelessWidget { + const LanguageSelection({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final languageCubit = context.watch(); + final locale = languageCubit.state; + int index = 0; + + if (locale.languageCode == 'az') { + index = 1; + } + + return Column( + children: [ + RadioListTile( + value: 0, + title: Text('English'), + groupValue: index, + onChanged: (index) { + languageCubit.changeLocale(Locale('en', 'US')); + }, + ), + RadioListTile( + value: 1, + title: Text('Azerbaijan'), + groupValue: index, + onChanged: (index) { + languageCubit.changeLocale(Locale('az', 'AZ')); + }, + ), + ], + ); + } +} diff --git a/lib/presentation/pages/settings/widgets/theme_selection.dart b/lib/presentation/pages/settings/widgets/theme_selection.dart new file mode 100644 index 0000000..7d04f2b --- /dev/null +++ b/lib/presentation/pages/settings/widgets/theme_selection.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../../bloc/theme/theme_cubit.dart'; + +class ThemeSelection extends StatelessWidget { + const ThemeSelection({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final themeCubit = context.watch(); + final themeMode = themeCubit.state; + + return Column( + children: [ + RadioListTile( + value: 0, + title: Text('System default'), + groupValue: themeMode.index, + onChanged: (index) { + themeCubit.changeTheme(ThemeMode.system); + }, + ), + RadioListTile( + value: 1, + title: Text('Light'), + groupValue: themeMode.index, + onChanged: (index) { + themeCubit.changeTheme(ThemeMode.light); + }, + ), + RadioListTile( + value: 2, + title: Text('Dark'), + groupValue: themeMode.index, + onChanged: (index) { + themeCubit.changeTheme(ThemeMode.dark); + }, + ), + ], + ); + } +} diff --git a/lib/presentation/pages/splash/splash_page.dart b/lib/presentation/pages/splash/splash_page.dart new file mode 100644 index 0000000..843c127 --- /dev/null +++ b/lib/presentation/pages/splash/splash_page.dart @@ -0,0 +1,47 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import '../../../constants/routes.dart'; + +class SplashPage extends StatefulWidget { + @override + _SplashPageState createState() => _SplashPageState(); +} + +class _SplashPageState extends State { + late Timer? timer; + + @override + void initState() { + super.initState(); + + timer = Timer.periodic(Duration(seconds: 2), (_) { + timer?.cancel(); + Navigator.popAndPushNamed(context, Routes.posts); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).primaryColor, + body: Center( + child: Text( + 'Splash Page', + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + ), + ); + } + + @override + void dispose() { + timer?.cancel(); + timer = null; + super.dispose(); + } +} diff --git a/lib/presentation/router/app_route_delegate.dart b/lib/presentation/router/app_route_delegate.dart deleted file mode 100644 index 0192bb0..0000000 --- a/lib/presentation/router/app_route_delegate.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../bloc/post/post_cubit.dart'; -import '../../constants/routes.dart'; -import '../../data/repositories/post_repository.dart'; -import '../pages/posts/posts_page.dart'; -import '../pages/settings/settings_page.dart'; - -class AppRouteDelegate extends RouterDelegate - with ChangeNotifier, PopNavigatorRouterDelegateMixin { - final _navigatorKey = GlobalKey(); - - @override - GlobalKey? get navigatorKey => _navigatorKey; - - final _pages = [ - MaterialPage( - child: RepositoryProvider( - create: (_) => PostRepository(), - child: BlocProvider( - create: (ctx) => PostCubit( - ctx.read(), - )..fetch(), - child: PostsPage(), - ), - ), - ), - ]; - - @override - Widget build(BuildContext context) { - return Navigator( - key: navigatorKey, - pages: _pages, - onPopPage: (route, result) { - if (route.didPop(result)) { - return true; - } - - return false; - }, - onGenerateRoute: (RouteSettings routeSettings) { - switch (routeSettings.name) { - case Routes.signIn: - break; - case Routes.posts: - return MaterialPageRoute( - builder: (_) => PostsPage(), - ); - case Routes.settings: - return MaterialPageRoute( - builder: (_) => SettingsPage(), - ); - default: - throw UnimplementedError( - 'Route: ${routeSettings.name} is not defined!', - ); - } - }, - ); - } - - @override - Future setNewRoutePath(String configuration) { - return SynchronousFuture(configuration); - } -} diff --git a/lib/presentation/router/app_route_information_parser.dart b/lib/presentation/router/app_route_information_parser.dart deleted file mode 100644 index 9b38dd1..0000000 --- a/lib/presentation/router/app_route_information_parser.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter/material.dart'; - -class AppRouteInformationParser extends RouteInformationParser { - @override - Future parseRouteInformation( - RouteInformation routeInformation) async { - return routeInformation.location!; - } -} diff --git a/lib/presentation/router/route_controller.dart b/lib/presentation/router/route_controller.dart new file mode 100644 index 0000000..2cdcd10 --- /dev/null +++ b/lib/presentation/router/route_controller.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../bloc/post/post_cubit.dart'; +import '../../constants/routes.dart'; +import '../../data/repositories/post_repository.dart'; +import '../pages/settings/settings_page.dart'; +import '../pages/posts/posts_page.dart'; + +class RouteController { + RouteController._(); + + static Route? onGenerateRoute(RouteSettings settings) { + switch (settings.name) { + case Routes.posts: + return MaterialPageRoute( + builder: (_) => RepositoryProvider( + create: (_) => PostRepository(), + child: BlocProvider( + create: (context) => PostCubit( + context.read(), + )..fetch(), + child: PostsPage(), + ), + ), + ); + case Routes.settings: + return MaterialPageRoute( + builder: (_) => SettingsPage(), + ); + default: + throw UnimplementedError(); + } + } +} From 5d06bf3a8525e22fafb773ba38c3977b543d3f83 Mon Sep 17 00:00:00 2001 From: yusubx Date: Wed, 5 May 2021 14:58:37 +0400 Subject: [PATCH 6/8] changed --- lib/bloc/comment/comment_cubit.dart | 8 ++++ lib/bloc/data/data_cubit.dart | 6 +-- lib/bloc/post/post_cubit.dart | 2 +- lib/config/init.dart | 2 + .../contractors/base_comment_repository.dart | 5 ++ .../contractors/base_post_repository.dart | 4 +- .../contractors/base_preferences_service.dart | 0 .../data_providers/comment_data_provider.dart | 14 ++++++ .../comment_data_provider.g.dart | 47 +++++++++++++++++++ lib/data/models/comment.dart | 25 ++++++++++ lib/data/repositories/comment_repository.dart | 13 +++++ lib/data/repositories/post_repository.dart | 2 +- .../pages/comments/comments_page.dart | 17 +++++++ 13 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 lib/bloc/comment/comment_cubit.dart create mode 100644 lib/data/contractors/base_comment_repository.dart rename lib/{ => data}/contractors/base_post_repository.dart (63%) rename lib/{ => data}/contractors/base_preferences_service.dart (100%) create mode 100644 lib/data/data_providers/comment_data_provider.dart create mode 100644 lib/data/data_providers/comment_data_provider.g.dart create mode 100644 lib/data/models/comment.dart create mode 100644 lib/data/repositories/comment_repository.dart create mode 100644 lib/presentation/pages/comments/comments_page.dart diff --git a/lib/bloc/comment/comment_cubit.dart b/lib/bloc/comment/comment_cubit.dart new file mode 100644 index 0000000..a7f17e9 --- /dev/null +++ b/lib/bloc/comment/comment_cubit.dart @@ -0,0 +1,8 @@ +import 'dart:async'; + +import '../data/data_cubit.dart'; + +class CommentsCubit extends DataCubit { + @override + FutureOr loadData([int? postId]) {} +} diff --git a/lib/bloc/data/data_cubit.dart b/lib/bloc/data/data_cubit.dart index 68cf13f..0591c10 100644 --- a/lib/bloc/data/data_cubit.dart +++ b/lib/bloc/data/data_cubit.dart @@ -13,13 +13,13 @@ abstract class DataCubit extends Cubit> { bool get emitInProgress => true; - FutureOr loadData(); + FutureOr loadData([int? id]); - void fetch() async { + void fetch([int? id]) async { emit(state.inProgress()); try { - final data = await loadData(); + final data = await loadData(id); if (data != null) { _logger.fine('$data'); diff --git a/lib/bloc/post/post_cubit.dart b/lib/bloc/post/post_cubit.dart index e0e6ee3..35809a6 100644 --- a/lib/bloc/post/post_cubit.dart +++ b/lib/bloc/post/post_cubit.dart @@ -13,7 +13,7 @@ class PostCubit extends DataCubit> { final BasePostRepository postRepository; @override - FutureOr> loadData() async { + FutureOr> loadData([int? id]) async { final posts = await postRepository.getPosts(); return posts diff --git a/lib/config/init.dart b/lib/config/init.dart index bb56d80..461e24d 100644 --- a/lib/config/init.dart +++ b/lib/config/init.dart @@ -8,6 +8,7 @@ import '../locator.dart'; import '../services/logging_service.dart'; import '../services/preferences_service.dart'; import 'config.dart'; +import '../data/data_providers/comment_data_provider.dart'; Future init() async { WidgetsFlutterBinding.ensureInitialized(); @@ -29,4 +30,5 @@ Future _initDataProviders() async { final dio = Dio(); locator.register(PostDataProvider(dio)); + locator.register(CommentDataProvider(dio)); } diff --git a/lib/data/contractors/base_comment_repository.dart b/lib/data/contractors/base_comment_repository.dart new file mode 100644 index 0000000..7337b37 --- /dev/null +++ b/lib/data/contractors/base_comment_repository.dart @@ -0,0 +1,5 @@ +import '../../data/models/comment.dart'; + +abstract class BaseCommentRepository { + Future> getComments({required int postId}); +} diff --git a/lib/contractors/base_post_repository.dart b/lib/data/contractors/base_post_repository.dart similarity index 63% rename from lib/contractors/base_post_repository.dart rename to lib/data/contractors/base_post_repository.dart index 376e5b2..2a287f4 100644 --- a/lib/contractors/base_post_repository.dart +++ b/lib/data/contractors/base_post_repository.dart @@ -1,5 +1,5 @@ -import '../data/models/post.dart'; +import '../../data/models/post.dart'; abstract class BasePostRepository { Future> getPosts(); -} \ No newline at end of file +} diff --git a/lib/contractors/base_preferences_service.dart b/lib/data/contractors/base_preferences_service.dart similarity index 100% rename from lib/contractors/base_preferences_service.dart rename to lib/data/contractors/base_preferences_service.dart diff --git a/lib/data/data_providers/comment_data_provider.dart b/lib/data/data_providers/comment_data_provider.dart new file mode 100644 index 0000000..9dc136b --- /dev/null +++ b/lib/data/data_providers/comment_data_provider.dart @@ -0,0 +1,14 @@ +import 'package:dio/dio.dart'; +import 'package:retrofit/retrofit.dart'; + +import '../models/comment.dart'; + +part 'comment_data_provider.g.dart'; + +@RestApi(baseUrl: 'https://jsonplaceholder.typicode.com/') +abstract class CommentDataProvider { + factory CommentDataProvider(Dio dio, {String baseUrl}) = _CommentDataProvider; + + @GET('/comments') + Future> getComments(@Query('postId') int postId); +} diff --git a/lib/data/data_providers/comment_data_provider.g.dart b/lib/data/data_providers/comment_data_provider.g.dart new file mode 100644 index 0000000..28831b5 --- /dev/null +++ b/lib/data/data_providers/comment_data_provider.g.dart @@ -0,0 +1,47 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'comment_data_provider.dart'; + +// ************************************************************************** +// RetrofitGenerator +// ************************************************************************** + +class _CommentDataProvider implements CommentDataProvider { + _CommentDataProvider(this._dio, {this.baseUrl}) { + baseUrl ??= 'https://jsonplaceholder.typicode.com/'; + } + + final Dio _dio; + + String? baseUrl; + + @override + Future> getComments(postId) async { + const _extra = {}; + final queryParameters = {r'postId': postId}; + final _data = {}; + final _result = await _dio.fetch>( + _setStreamType>( + Options(method: 'GET', headers: {}, extra: _extra) + .compose(_dio.options, '/comments', + queryParameters: queryParameters, data: _data) + .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); + var value = _result.data! + .map((dynamic i) => Comment.fromJson(i as Map)) + .toList(); + return value; + } + + RequestOptions _setStreamType(RequestOptions requestOptions) { + if (T != dynamic && + !(requestOptions.responseType == ResponseType.bytes || + requestOptions.responseType == ResponseType.stream)) { + if (T == String) { + requestOptions.responseType = ResponseType.plain; + } else { + requestOptions.responseType = ResponseType.json; + } + } + return requestOptions; + } +} diff --git a/lib/data/models/comment.dart b/lib/data/models/comment.dart new file mode 100644 index 0000000..c0918d1 --- /dev/null +++ b/lib/data/models/comment.dart @@ -0,0 +1,25 @@ +class Comment { + final int postId; + final int id; + final String name; + final String email; + String body; + + Comment({ + required this.postId, + required this.id, + required this.name, + required this.email, + required this.body, + }); + + factory Comment.fromJson(Map json) { + return Comment( + postId: json['postId'], + id: json['id'], + name: json['name'], + email: json['email'], + body: json['body'], + ); + } +} diff --git a/lib/data/repositories/comment_repository.dart b/lib/data/repositories/comment_repository.dart new file mode 100644 index 0000000..4a871ab --- /dev/null +++ b/lib/data/repositories/comment_repository.dart @@ -0,0 +1,13 @@ +import 'package:architecture_example/data/models/comment.dart'; + +import '../../locator.dart'; +import '../contractors/base_comment_repository.dart'; +import '../data_providers/comment_data_provider.dart'; + +class CommentRepository implements BaseCommentRepository { + final _commentDataProvider = Locator.instance.get(); + + @override + Future> getComments({required int postId}) => + _commentDataProvider.getComments(postId); +} diff --git a/lib/data/repositories/post_repository.dart b/lib/data/repositories/post_repository.dart index 4a296c4..62c0b54 100644 --- a/lib/data/repositories/post_repository.dart +++ b/lib/data/repositories/post_repository.dart @@ -1,7 +1,7 @@ import 'package:architecture_example/data/models/post.dart'; -import '../../contractors/base_post_repository.dart'; import '../../locator.dart'; +import '../contractors/base_post_repository.dart'; import '../data_providers/post_data_provider.dart'; class PostRepository implements BasePostRepository { diff --git a/lib/presentation/pages/comments/comments_page.dart b/lib/presentation/pages/comments/comments_page.dart new file mode 100644 index 0000000..4270151 --- /dev/null +++ b/lib/presentation/pages/comments/comments_page.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class CommentsPage extends StatelessWidget { + const CommentsPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Comments'), + ), + body: SafeArea( + child: ListView(), + ), + ); + } +} From badd49aa93e3a366763f43f4819509475142a80e Mon Sep 17 00:00:00 2001 From: yusubx Date: Wed, 5 May 2021 15:02:28 +0400 Subject: [PATCH 7/8] changed --- lib/app.dart | 4 ++-- lib/bloc/language/language_cubit.dart | 2 +- lib/bloc/post/post_cubit.dart | 4 ++-- lib/bloc/theme/theme_cubit.dart | 2 +- lib/{ => data}/exceptions/network_exceptions.dart | 0 lib/presentation/pages/posts/widgets/more_menu.dart | 4 ++-- lib/presentation/pages/splash/splash_page.dart | 2 +- lib/presentation/router/route_controller.dart | 2 +- lib/services/preferences_service.dart | 2 +- lib/{ => utils}/constants/app_colors.dart | 0 lib/{ => utils}/constants/app_text_styles.dart | 0 lib/{ => utils}/constants/app_themes.dart | 0 lib/{ => utils}/constants/routes.dart | 0 lib/{ => utils}/constants/supported_locales.dart | 0 test/mocks/mock_preferences_service.dart | 2 +- 15 files changed, 12 insertions(+), 12 deletions(-) rename lib/{ => data}/exceptions/network_exceptions.dart (100%) rename lib/{ => utils}/constants/app_colors.dart (100%) rename lib/{ => utils}/constants/app_text_styles.dart (100%) rename lib/{ => utils}/constants/app_themes.dart (100%) rename lib/{ => utils}/constants/routes.dart (100%) rename lib/{ => utils}/constants/supported_locales.dart (100%) diff --git a/lib/app.dart b/lib/app.dart index ff9e253..0087bbf 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -5,10 +5,10 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'bloc/language/language_cubit.dart'; import 'bloc/theme/theme_cubit.dart'; -import 'constants/app_themes.dart'; -import 'constants/supported_locales.dart'; import 'presentation/pages/splash/splash_page.dart'; import 'presentation/router/route_controller.dart'; +import 'utils/constants/app_themes.dart'; +import 'utils/constants/supported_locales.dart'; class App extends StatelessWidget { @override diff --git a/lib/bloc/language/language_cubit.dart b/lib/bloc/language/language_cubit.dart index 60df02f..b2eb58d 100644 --- a/lib/bloc/language/language_cubit.dart +++ b/lib/bloc/language/language_cubit.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../contractors/base_preferences_service.dart'; +import '../../data/contractors/base_preferences_service.dart'; class LanguageCubit extends Cubit { LanguageCubit(this.preferencesService) : super(Locale('en', 'US')) { diff --git a/lib/bloc/post/post_cubit.dart b/lib/bloc/post/post_cubit.dart index 35809a6..b16e3b8 100644 --- a/lib/bloc/post/post_cubit.dart +++ b/lib/bloc/post/post_cubit.dart @@ -1,11 +1,11 @@ import 'dart:async'; -import '../../contractors/base_post_repository.dart'; +import '../../data/contractors/base_post_repository.dart'; import '../../data/models/post.dart'; import '../data/data_cubit.dart'; -export '../data/data_cubit.dart'; export '../../data/models/post.dart'; +export '../data/data_cubit.dart'; class PostCubit extends DataCubit> { PostCubit(this.postRepository); diff --git a/lib/bloc/theme/theme_cubit.dart b/lib/bloc/theme/theme_cubit.dart index ca8888d..d5ec5b4 100644 --- a/lib/bloc/theme/theme_cubit.dart +++ b/lib/bloc/theme/theme_cubit.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../contractors/base_preferences_service.dart'; +import '../../data/contractors/base_preferences_service.dart'; class ThemeCubit extends Cubit { ThemeCubit(this.preferencesService) : super(ThemeMode.system) { diff --git a/lib/exceptions/network_exceptions.dart b/lib/data/exceptions/network_exceptions.dart similarity index 100% rename from lib/exceptions/network_exceptions.dart rename to lib/data/exceptions/network_exceptions.dart diff --git a/lib/presentation/pages/posts/widgets/more_menu.dart b/lib/presentation/pages/posts/widgets/more_menu.dart index 1566df1..64db7ad 100644 --- a/lib/presentation/pages/posts/widgets/more_menu.dart +++ b/lib/presentation/pages/posts/widgets/more_menu.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../../constants/routes.dart'; +import '../../../../utils/constants/routes.dart'; class MoreMenu extends StatelessWidget { const MoreMenu({Key? key}) : super(key: key); @@ -21,7 +21,7 @@ class MoreMenu extends StatelessWidget { value: 'Settings', child: Text('Settings'), ), - PopupMenuDivider(), + PopupMenuDivider(), PopupMenuItem( value: 'About', child: Text('About'), diff --git a/lib/presentation/pages/splash/splash_page.dart b/lib/presentation/pages/splash/splash_page.dart index 843c127..3fd8618 100644 --- a/lib/presentation/pages/splash/splash_page.dart +++ b/lib/presentation/pages/splash/splash_page.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import '../../../constants/routes.dart'; +import '../../../utils/constants/routes.dart'; class SplashPage extends StatefulWidget { @override diff --git a/lib/presentation/router/route_controller.dart b/lib/presentation/router/route_controller.dart index 2cdcd10..c721bc1 100644 --- a/lib/presentation/router/route_controller.dart +++ b/lib/presentation/router/route_controller.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../bloc/post/post_cubit.dart'; -import '../../constants/routes.dart'; +import '../../utils/constants/routes.dart'; import '../../data/repositories/post_repository.dart'; import '../pages/settings/settings_page.dart'; import '../pages/posts/posts_page.dart'; diff --git a/lib/services/preferences_service.dart b/lib/services/preferences_service.dart index 992e759..bb4f8d9 100644 --- a/lib/services/preferences_service.dart +++ b/lib/services/preferences_service.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../contractors/base_preferences_service.dart'; +import '../data/contractors/base_preferences_service.dart'; class PreferencesService implements BasePreferencesService { PreferencesService._(); diff --git a/lib/constants/app_colors.dart b/lib/utils/constants/app_colors.dart similarity index 100% rename from lib/constants/app_colors.dart rename to lib/utils/constants/app_colors.dart diff --git a/lib/constants/app_text_styles.dart b/lib/utils/constants/app_text_styles.dart similarity index 100% rename from lib/constants/app_text_styles.dart rename to lib/utils/constants/app_text_styles.dart diff --git a/lib/constants/app_themes.dart b/lib/utils/constants/app_themes.dart similarity index 100% rename from lib/constants/app_themes.dart rename to lib/utils/constants/app_themes.dart diff --git a/lib/constants/routes.dart b/lib/utils/constants/routes.dart similarity index 100% rename from lib/constants/routes.dart rename to lib/utils/constants/routes.dart diff --git a/lib/constants/supported_locales.dart b/lib/utils/constants/supported_locales.dart similarity index 100% rename from lib/constants/supported_locales.dart rename to lib/utils/constants/supported_locales.dart diff --git a/test/mocks/mock_preferences_service.dart b/test/mocks/mock_preferences_service.dart index 5bbe74b..0c52cb6 100644 --- a/test/mocks/mock_preferences_service.dart +++ b/test/mocks/mock_preferences_service.dart @@ -1,4 +1,4 @@ -import 'package:architecture_example/contractors/base_preferences_service.dart'; +import 'package:architecture_example/data/contractors/base_preferences_service.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/material/app.dart'; import 'package:shared_preferences/shared_preferences.dart'; From 65e5f41dd641568cc1d7d89f434e340385730368 Mon Sep 17 00:00:00 2001 From: yusubx Date: Thu, 6 May 2021 12:16:58 +0400 Subject: [PATCH 8/8] comments page added --- lib/bloc/comment/comment_cubit.dart | 13 ++++- lib/config/init.dart | 6 +- lib/data/exceptions/network_exceptions.dart | 1 + lib/{ => data}/services/logging_service.dart | 0 .../services/preferences_service.dart | 2 +- lib/l10n/app_az.arb | 3 +- lib/main.dart | 2 +- lib/presentation/global/.gitkeep | 0 .../post_item.dart => global/data_item.dart} | 56 +++++++++++-------- .../pages/comments/comments_page.dart | 35 ++++++++++-- lib/presentation/pages/posts/posts_page.dart | 24 +++++--- .../pages/settings/settings_page.dart | 11 +++- lib/presentation/router/route_controller.dart | 23 +++++++- lib/utils/constants/app_text_styles.dart | 9 +++ lib/utils/constants/app_themes.dart | 4 ++ lib/utils/constants/routes.dart | 1 + lib/utils/extensions/theme_ext.dart | 6 ++ lib/utils/mixins/walk_mixin.dart | 15 +++++ 18 files changed, 164 insertions(+), 47 deletions(-) rename lib/{ => data}/services/logging_service.dart (100%) rename lib/{ => data}/services/preferences_service.dart (96%) delete mode 100644 lib/presentation/global/.gitkeep rename lib/presentation/{pages/posts/widgets/post_item.dart => global/data_item.dart} (58%) create mode 100644 lib/utils/extensions/theme_ext.dart create mode 100644 lib/utils/mixins/walk_mixin.dart diff --git a/lib/bloc/comment/comment_cubit.dart b/lib/bloc/comment/comment_cubit.dart index a7f17e9..57e7162 100644 --- a/lib/bloc/comment/comment_cubit.dart +++ b/lib/bloc/comment/comment_cubit.dart @@ -1,8 +1,17 @@ import 'dart:async'; +import '../../data/contractors/base_comment_repository.dart'; +import '../../data/models/comment.dart'; import '../data/data_cubit.dart'; -class CommentsCubit extends DataCubit { +export '../data/data_cubit.dart'; + +class CommentCubit extends DataCubit> { + CommentCubit(this.baseCommentRepository); + + final BaseCommentRepository baseCommentRepository; + @override - FutureOr loadData([int? postId]) {} + FutureOr> loadData([int? postId]) => + baseCommentRepository.getComments(postId: postId!); } diff --git a/lib/config/init.dart b/lib/config/init.dart index 461e24d..11f8559 100644 --- a/lib/config/init.dart +++ b/lib/config/init.dart @@ -3,12 +3,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../bloc/app_bloc_observer.dart'; +import '../data/data_providers/comment_data_provider.dart'; import '../data/data_providers/post_data_provider.dart'; +import '../data/services/logging_service.dart'; +import '../data/services/preferences_service.dart'; import '../locator.dart'; -import '../services/logging_service.dart'; -import '../services/preferences_service.dart'; import 'config.dart'; -import '../data/data_providers/comment_data_provider.dart'; Future init() async { WidgetsFlutterBinding.ensureInitialized(); diff --git a/lib/data/exceptions/network_exceptions.dart b/lib/data/exceptions/network_exceptions.dart index e69de29..8b13789 100644 --- a/lib/data/exceptions/network_exceptions.dart +++ b/lib/data/exceptions/network_exceptions.dart @@ -0,0 +1 @@ + diff --git a/lib/services/logging_service.dart b/lib/data/services/logging_service.dart similarity index 100% rename from lib/services/logging_service.dart rename to lib/data/services/logging_service.dart diff --git a/lib/services/preferences_service.dart b/lib/data/services/preferences_service.dart similarity index 96% rename from lib/services/preferences_service.dart rename to lib/data/services/preferences_service.dart index bb4f8d9..992e759 100644 --- a/lib/services/preferences_service.dart +++ b/lib/data/services/preferences_service.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../data/contractors/base_preferences_service.dart'; +import '../contractors/base_preferences_service.dart'; class PreferencesService implements BasePreferencesService { PreferencesService._(); diff --git a/lib/l10n/app_az.arb b/lib/l10n/app_az.arb index 2374603..c0e7f60 100644 --- a/lib/l10n/app_az.arb +++ b/lib/l10n/app_az.arb @@ -1,4 +1,5 @@ { "posts": "Postlar", - "profile": "Profil" + "profile": "Profil", + "setttings": "Tenzimlemeler" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 406534a..9cba18c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,7 @@ import 'app.dart'; import 'bloc/language/language_cubit.dart'; import 'bloc/theme/theme_cubit.dart'; import 'config/init.dart'; -import 'services/preferences_service.dart'; +import 'data/services/preferences_service.dart'; void main() async { await init(); diff --git a/lib/presentation/global/.gitkeep b/lib/presentation/global/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/presentation/pages/posts/widgets/post_item.dart b/lib/presentation/global/data_item.dart similarity index 58% rename from lib/presentation/pages/posts/widgets/post_item.dart rename to lib/presentation/global/data_item.dart index 5663556..752fcea 100644 --- a/lib/presentation/pages/posts/widgets/post_item.dart +++ b/lib/presentation/global/data_item.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import '../../../../data/models/post.dart'; -import 'action_button.dart'; + +import '../pages/posts/widgets/action_button.dart'; final avatarColors = { 1: Colors.red, @@ -16,13 +16,22 @@ final avatarColors = { 10: Colors.indigo, }; -class PostItem extends StatelessWidget { - const PostItem({ +class DataItem extends StatelessWidget { + const DataItem({ Key? key, - required this.post, + required this.id, + required this.body, + this.onLikeTap, + this.onCommentTap, + this.showActionBar = true, }) : super(key: key); - final Post post; + final T id; + final String body; + final VoidCallback? onLikeTap; + final VoidCallback? onCommentTap; + + final bool showActionBar; @override Widget build(BuildContext context) { @@ -51,32 +60,33 @@ class PostItem extends StatelessWidget { Row( children: [ CircleAvatar( - backgroundColor: avatarColors[post.userId], + backgroundColor: avatarColors[body.length % 10], ), const SizedBox(width: 10), - Text('User ${post.userId}') + Text('$id'), ], ), const SizedBox(height: 10), Text( - post.body, + body, textAlign: TextAlign.justify, ), const SizedBox(height: 10), - Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ActionButton( - icon: CupertinoIcons.hand_thumbsup, - onTap: () {}, - ), - ActionButton( - icon: CupertinoIcons.bubble_left, - onTap: () {}, - ), - ], - ), + if (showActionBar) Divider(), + if (showActionBar) + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ActionButton( + icon: CupertinoIcons.hand_thumbsup, + onTap: onLikeTap, + ), + ActionButton( + icon: CupertinoIcons.bubble_left, + onTap: onCommentTap, + ), + ], + ), ], ), ); diff --git a/lib/presentation/pages/comments/comments_page.dart b/lib/presentation/pages/comments/comments_page.dart index 4270151..519bb7e 100644 --- a/lib/presentation/pages/comments/comments_page.dart +++ b/lib/presentation/pages/comments/comments_page.dart @@ -1,17 +1,44 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../bloc/comment/comment_cubit.dart'; +import '../../global/data_item.dart'; class CommentsPage extends StatelessWidget { const CommentsPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + final commentState = context.watch().state; + + late final child; + + if (commentState.isInProgress) { + child = Center(child: CircularProgressIndicator()); + } else if (commentState.isSuccess) { + child = ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8.0), + itemBuilder: (_, i) { + final comment = commentState.data?[i]; + + return DataItem( + id: comment!.postId, + body: comment.body, + showActionBar: false, + ); + }, + itemCount: commentState.data!.length, + ); + } else if (commentState.isFailure) { + child = Center(child: Text('ERROR!')); + } + return Scaffold( appBar: AppBar( - title: Text('Comments'), - ), - body: SafeArea( - child: ListView(), + title: Text( + 'Comments', + ), ), + body: SafeArea(child: child), ); } } diff --git a/lib/presentation/pages/posts/posts_page.dart b/lib/presentation/pages/posts/posts_page.dart index 522dcb9..0ba58dc 100644 --- a/lib/presentation/pages/posts/posts_page.dart +++ b/lib/presentation/pages/posts/posts_page.dart @@ -4,21 +4,20 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../bloc/post/post_cubit.dart'; +import '../../../utils/constants/routes.dart'; +import '../../../utils/extensions/theme_ext.dart'; import '../../../utils/extensions/waitable_cubit_ext.dart'; +import '../../global/data_item.dart'; import 'widgets/home_bottom_bar.dart'; import 'widgets/more_menu.dart'; -import 'widgets/post_item.dart'; class PostsPage extends StatelessWidget { @override Widget build(BuildContext context) { - final mainColor = Theme.of(context).brightness == Brightness.dark - ? Theme.of(context).cardColor - : Theme.of(context).primaryColorDark; - return AnnotatedRegion( value: SystemUiOverlayStyle( - statusBarColor: mainColor, + statusBarColor: Theme.of(context).statusBarColor, + statusBarIconBrightness: Brightness.light, ), child: Scaffold( body: SafeArea( @@ -28,7 +27,7 @@ class PostsPage extends StatelessWidget { title: Text( AppLocalizations.of(context)!.posts, ), - backgroundColor: mainColor, + backgroundColor: Theme.of(context).statusBarColor, actions: [MoreMenu()], ), SliverFillRemaining( @@ -56,7 +55,16 @@ class PostsPage extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 8.0), itemBuilder: (_, i) { final post = posts[i]; - return PostItem(post: post); + + return DataItem( + id: post.userId, + body: post.body, + onCommentTap: () => Navigator.pushNamed( + context, + Routes.comments, + arguments: post.id, + ), + ); }, itemCount: posts.length, ); diff --git a/lib/presentation/pages/settings/settings_page.dart b/lib/presentation/pages/settings/settings_page.dart index 1fbc045..12a02c2 100644 --- a/lib/presentation/pages/settings/settings_page.dart +++ b/lib/presentation/pages/settings/settings_page.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; -import 'widgets/theme_selection.dart'; +import 'package:flutter/services.dart'; + +import '../../../utils/extensions/theme_ext.dart'; import 'widgets/language_selection.dart'; +import 'widgets/theme_selection.dart'; class SettingsPage extends StatefulWidget { const SettingsPage({Key? key}) : super(key: key); @@ -18,6 +21,12 @@ class _SettingsPageState extends State { return Scaffold( appBar: AppBar( title: Text('Settings'), + backwardsCompatibility: false, + backgroundColor: Theme.of(context).statusBarColor, + brightness: Brightness.light, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarColor: Theme.of(context).statusBarColor, + ), ), body: SafeArea( child: SingleChildScrollView( diff --git a/lib/presentation/router/route_controller.dart b/lib/presentation/router/route_controller.dart index c721bc1..0eadb23 100644 --- a/lib/presentation/router/route_controller.dart +++ b/lib/presentation/router/route_controller.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../bloc/comment/comment_cubit.dart'; import '../../bloc/post/post_cubit.dart'; -import '../../utils/constants/routes.dart'; +import '../../data/repositories/comment_repository.dart'; import '../../data/repositories/post_repository.dart'; -import '../pages/settings/settings_page.dart'; +import '../../utils/constants/routes.dart'; +import '../pages/comments/comments_page.dart'; import '../pages/posts/posts_page.dart'; +import '../pages/settings/settings_page.dart'; class RouteController { RouteController._(); @@ -24,12 +27,26 @@ class RouteController { ), ), ); + case Routes.comments: + final postId = settings.arguments as int; + + return MaterialPageRoute( + builder: (_) => RepositoryProvider( + create: (_) => CommentRepository(), + child: BlocProvider( + create: (context) => CommentCubit( + context.read(), + )..fetch(postId), + child: CommentsPage(), + ), + ), + ); case Routes.settings: return MaterialPageRoute( builder: (_) => SettingsPage(), ); default: - throw UnimplementedError(); + throw UnimplementedError('Routes is not defined for ${settings.name}!'); } } } diff --git a/lib/utils/constants/app_text_styles.dart b/lib/utils/constants/app_text_styles.dart index b88558b..02b8e3e 100644 --- a/lib/utils/constants/app_text_styles.dart +++ b/lib/utils/constants/app_text_styles.dart @@ -1,3 +1,12 @@ +import 'package:flutter/material.dart'; + class AppTextStyles { AppTextStyles._(); + + // Poppins - 12 w500 + // Poppins - 12 w500 + // Poppins - 16 w600 + + static final poppinsw500 = TextStyle(fontWeight: FontWeight.w500); + static final poppinsw600 = TextStyle(fontWeight: FontWeight.w600); } diff --git a/lib/utils/constants/app_themes.dart b/lib/utils/constants/app_themes.dart index aa24767..5b8bb60 100644 --- a/lib/utils/constants/app_themes.dart +++ b/lib/utils/constants/app_themes.dart @@ -1,3 +1,4 @@ +import 'package:architecture_example/utils/constants/app_text_styles.dart'; import 'package:flutter/material.dart'; import 'app_colors.dart'; @@ -14,6 +15,9 @@ abstract class AppThemes { bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedItemColor: AppColors.primaryColor, ), + textTheme: TextTheme( + bodyText1: AppTextStyles.poppinsw500.copyWith(fontSize: 16), + ), ); static final darkTheme = ThemeData( diff --git a/lib/utils/constants/routes.dart b/lib/utils/constants/routes.dart index f7cc6a3..d32652e 100644 --- a/lib/utils/constants/routes.dart +++ b/lib/utils/constants/routes.dart @@ -4,6 +4,7 @@ class Routes { static const String splash = '/splash'; static const String signIn = '/signIn'; static const String posts = '/posts'; + static const String comments = '/comments'; static const String settings = '/settings'; static const String about = '/about'; } diff --git a/lib/utils/extensions/theme_ext.dart b/lib/utils/extensions/theme_ext.dart new file mode 100644 index 0000000..a9fc13c --- /dev/null +++ b/lib/utils/extensions/theme_ext.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; + +extension ThemeExt on ThemeData { + Color get statusBarColor => + brightness == Brightness.dark ? cardColor : primaryColorDark; +} diff --git a/lib/utils/mixins/walk_mixin.dart b/lib/utils/mixins/walk_mixin.dart new file mode 100644 index 0000000..b3361cc --- /dev/null +++ b/lib/utils/mixins/walk_mixin.dart @@ -0,0 +1,15 @@ +mixin WalkMixin { + void walk() {} +} + +mixin SwimMixin { + void swim() {} +} + +class APerson with WalkMixin, SwimMixin {} + +void test() { + final a = APerson(); + a.walk(); + a.swim(); +}