diff --git a/android/gradle.properties b/android/gradle.properties index dcf12984c36f3453d790ee3d76b4953ce8cb7158..33c43775a7db70e59256cad64931ad4fc01c2c7c 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=1.0.22 -app.versionCode=23 +app.versionName=1.0.23 +app.versionCode=24 diff --git a/assets/translations/en.json b/assets/translations/en.json index f5e702ddc07406ea3f4ac0f7713b58bcf238b344..ca94af1202f50913ca29861c168ca3a6813f3578 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,5 +1,7 @@ { "app_name": "Random application", + "bottom_nav_sample": "Sample", + "bottom_nav_chart": "Graph", "TOP": "TOP", "BOTTOM": "BOTTOM" diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 1b75c0926fdd377711092fdc992a54b143e2af2b..6016e93819e90cfbf849b98f265175c97ba97433 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -1,5 +1,7 @@ { "app_name": "Random application", + "bottom_nav_sample": "Démo", + "bottom_nav_chart": "Graph", "TOP": "HAUT", "BOTTOM": "BAS" diff --git a/lib/activities/ActivityDemoPage.dart b/lib/activities/ActivityDemoPage.dart index b8cceb192700ed2174a1da9b0900e50f99340f2b..d6c1dba48e3c98c612b14195dda0f4dfc02fb970 100644 --- a/lib/activities/ActivityDemoPage.dart +++ b/lib/activities/ActivityDemoPage.dart @@ -2,39 +2,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; class ActivityDemoPage extends StatelessWidget { - static const String code = 'demo'; - static const String route = '/' + code; + const ActivityDemoPage({super.key}); @override Widget build(BuildContext context) { - Scaffold pageContent = Scaffold( - appBar: AppBar( - elevation: 0, - actions: <Widget>[ - IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - print('reset activity'); - Navigator.pop(context); - }, - ), - ], - ), - backgroundColor: Theme.of(context).colorScheme.background, - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: <Widget>[ - Text('TOP').tr(), - SizedBox(height: 2), - Text('BOTTOM').tr(), - ], - ), - ), - ); - return SizedBox.expand( child: Container( child: FittedBox( @@ -43,7 +14,18 @@ class ActivityDemoPage extends StatelessWidget { child: SizedBox( height: (MediaQuery.of(context).size.height), width: (MediaQuery.of(context).size.width), - child: pageContent, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: <Widget>[ + Text('TOP').tr(), + SizedBox(height: 2), + Text('BOTTOM').tr(), + ], + ), + ), ), ), ), diff --git a/lib/activities/ActivityGraphPage.dart b/lib/activities/ActivityGraphPage.dart index da60d7ba89f4c69d0589f3efe0a42de5ef14fa1f..33fb267fcc2121e9e81f2df2477f74821669f3e6 100644 --- a/lib/activities/ActivityGraphPage.dart +++ b/lib/activities/ActivityGraphPage.dart @@ -2,56 +2,12 @@ import 'package:flutter/material.dart'; import 'package:random/painters/GraphPainter.dart'; class ActivityGraphPage extends StatelessWidget { - static const String code = 'graph'; - static const String route = '/' + code; + const ActivityGraphPage({super.key}); @override Widget build(BuildContext context) { double boardWidth = MediaQuery.of(context).size.width; - Scaffold pageContent = Scaffold( - appBar: AppBar( - elevation: 0, - actions: <Widget>[ - IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - print('reset activity'); - Navigator.pop(context); - }, - ), - ], - ), - backgroundColor: Theme.of(context).colorScheme.background, - body: SafeArea( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Center( - child: GestureDetector( - onTapUp: (details) { - double xTap = details.localPosition.dx; - double yTap = details.localPosition.dy; - print('[' + xTap.toString() + ',' + yTap.toString() + ']'); - }, - child: Container( - margin: EdgeInsets.all(4), - padding: EdgeInsets.all(4), - child: CustomPaint( - size: Size(boardWidth, boardWidth), - willChange: false, - painter: GraphPainter(), - isComplex: true, - ), - ), - ), - ), - ], - ), - ), - ); - return SizedBox.expand( child: Container( child: FittedBox( @@ -60,7 +16,33 @@ class ActivityGraphPage extends StatelessWidget { child: SizedBox( height: (MediaQuery.of(context).size.height), width: (MediaQuery.of(context).size.width), - child: pageContent, + child: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center( + child: GestureDetector( + onTapUp: (details) { + double xTap = details.localPosition.dx; + double yTap = details.localPosition.dy; + print('[' + xTap.toString() + ',' + yTap.toString() + ']'); + }, + child: Container( + margin: EdgeInsets.all(4), + padding: EdgeInsets.all(4), + child: CustomPaint( + size: Size(boardWidth, boardWidth), + willChange: false, + painter: GraphPainter(), + isComplex: true, + ), + ), + ), + ), + ], + ), + ), ), ), ), diff --git a/lib/cubit/bottom_nav_cubit.dart b/lib/cubit/bottom_nav_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..7a39813bfd5dd444a4b0ff8f4763acd0f1a5a47a --- /dev/null +++ b/lib/cubit/bottom_nav_cubit.dart @@ -0,0 +1,21 @@ +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +class BottomNavCubit extends HydratedCubit<int> { + BottomNavCubit() : super(0); + + void updateIndex(int index) => emit(index); + + void getFirstScreen() => emit(0); + + void getSecondScreen() => emit(1); + + @override + int? fromJson(Map<String, dynamic> json) { + return json['pageIndex'] as int?; + } + + @override + Map<String, dynamic>? toJson(int state) { + return <String, int>{'pageIndex': state}; + } +} diff --git a/lib/main.dart b/lib/main.dart index 82c70b7bb1ce8367ffa13601eb0b132d0f5acfe9..e7d191c3c40a776bda596b322c26c69ca4a44509 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,14 +1,23 @@ +import 'dart:io'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:random/ui/screens/skeleton_screen.dart'; import 'package:random/config/theme.dart'; -import 'package:random/activities/ActivityDemoPage.dart'; -import 'package:random/activities/ActivityGraphPage.dart'; void main() async { + /// Initialize packages WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); + final Directory tmpDir = await getTemporaryDirectory(); + Hive.init(tmpDir.toString()); + HydratedBloc.storage = await HydratedStorage.build( + storageDirectory: tmpDir, + ); runApp( EasyLocalization( @@ -33,32 +42,6 @@ class MyApp extends StatelessWidget { title: 'Random application', theme: appTheme, home: SkeletonScreen(), - onGenerateRoute: (settings) { - switch (settings.name) { - case ActivityDemoPage.route: - { - return MaterialPageRoute( - builder: (context) => ActivityDemoPage(), - ); - } - case ActivityGraphPage.route: - { - return MaterialPageRoute( - builder: (context) => ActivityGraphPage(), - ); - } - - default: - { - print("Unknown menu entry: " + settings.name.toString()); - } - break; - } - - return null; - }, - - // Localization stuff localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, diff --git a/lib/ui/screens/skeleton_screen.dart b/lib/ui/screens/skeleton_screen.dart index cd13b088efda89f5714ca58c0fe3c7296fb5b1af..6d6e9faac7f6a8b3f50c41ff596fbc461560f559 100644 --- a/lib/ui/screens/skeleton_screen.dart +++ b/lib/ui/screens/skeleton_screen.dart @@ -1,55 +1,36 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:random/activities/ActivityDemoPage.dart'; import 'package:random/activities/ActivityGraphPage.dart'; +import 'package:random/cubit/bottom_nav_cubit.dart'; +import 'package:random/ui/widgets/custom_app_bar.dart'; +import 'package:random/ui/widgets/bottom_nav_bar.dart'; class SkeletonScreen extends StatelessWidget { - static const String id = 'home'; + const SkeletonScreen({super.key}); + @override Widget build(BuildContext context) { - double screenWidth = MediaQuery.of(context).size.width; + const List<Widget> pageNavigation = <Widget>[ + ActivityDemoPage(), + ActivityGraphPage(), + ]; - Container _menuItem(String activityCode) { - double imageSize = screenWidth / 4; - - String imageAsset = 'assets/menu/' + activityCode + '.png'; - - return Container( - margin: EdgeInsets.all(2), - child: TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.all(4), - backgroundColor: Theme.of(context).colorScheme.background, - ), - child: Image( - image: AssetImage(imageAsset), - width: imageSize, - height: imageSize, - fit: BoxFit.fill, - ), - onPressed: () { - Navigator.pushNamed( - context, - '/' + activityCode, - ); + return BlocProvider<BottomNavCubit>( + create: (BuildContext context) => BottomNavCubit(), + child: Scaffold( + extendBodyBehindAppBar: true, + appBar: const CustomAppBarGone(), + body: BlocBuilder<BottomNavCubit, int>( + builder: (BuildContext context, int state) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: pageNavigation.elementAt(state)); }, ), - ); - } - - return Scaffold( - extendBodyBehindAppBar: true, - backgroundColor: Theme.of(context).colorScheme.background, - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: <Widget>[ - _menuItem(ActivityDemoPage.code), - _menuItem(ActivityGraphPage.code), - ], - ), + bottomNavigationBar: const BottomNavBar(), + backgroundColor: Theme.of(context).colorScheme.background, ), ); } diff --git a/lib/ui/widgets/bottom_nav_bar.dart b/lib/ui/widgets/bottom_nav_bar.dart new file mode 100644 index 0000000000000000000000000000000000000000..d656854e1934bd014c1a053286b45bbf0a4ec723 --- /dev/null +++ b/lib/ui/widgets/bottom_nav_bar.dart @@ -0,0 +1,47 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:ionicons/ionicons.dart'; + +import 'package:random/cubit/bottom_nav_cubit.dart'; + +class BottomNavBar extends StatelessWidget { + const BottomNavBar({super.key}); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.only(top: 1, right: 4, left: 4), + elevation: 4, + shadowColor: Theme.of(context).colorScheme.shadow, + color: Theme.of(context).colorScheme.surfaceVariant, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), + child: BlocBuilder<BottomNavCubit, int>(builder: (BuildContext context, int state) { + return BottomNavigationBar( + currentIndex: state, + onTap: (int index) => context.read<BottomNavCubit>().updateIndex(index), + type: BottomNavigationBarType.fixed, + elevation: 0, + backgroundColor: Colors.transparent, + selectedItemColor: Theme.of(context).colorScheme.primary, + unselectedItemColor: Theme.of(context).textTheme.bodySmall!.color, + items: <BottomNavigationBarItem>[ + BottomNavigationBarItem( + icon: const Icon(Ionicons.image_outline), + label: tr('bottom_nav_sample'), + ), + BottomNavigationBarItem( + icon: const Icon(Ionicons.pencil_outline), + label: tr('bottom_nav_chart'), + ), + ], + ); + }), + ); + } +} diff --git a/lib/ui/widgets/custom_app_bar.dart b/lib/ui/widgets/custom_app_bar.dart new file mode 100644 index 0000000000000000000000000000000000000000..93c477e9160589a31be400f5542e7809727851bf --- /dev/null +++ b/lib/ui/widgets/custom_app_bar.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class CustomAppBarGone extends StatelessWidget implements PreferredSizeWidget { + const CustomAppBarGone({super.key}); + + @override + Widget build(BuildContext context) { + final Brightness brightness = Theme.of(context).colorScheme.brightness; + + return AppBar( + systemOverlayStyle: SystemUiOverlayStyle( + statusBarBrightness: brightness, + systemStatusBarContrastEnforced: false, + statusBarColor: Theme.of(context).colorScheme.background, + statusBarIconBrightness: + brightness == Brightness.dark ? Brightness.light : Brightness.dark, + ), + backgroundColor: Colors.transparent, + excludeHeaderSemantics: true, + shadowColor: Colors.transparent, + scrolledUnderElevation: 0, + surfaceTintColor: Colors.transparent, + foregroundColor: Colors.transparent, + elevation: 0, + bottomOpacity: 0, + toolbarOpacity: 0, + ); + } + + @override + Size get preferredSize => const Size.fromHeight(0); +} diff --git a/pubspec.lock b/pubspec.lock index 307e7a3551b7bb03020b135cef2c6e8494646455..f0fbed261d7a349b9ff0b9b06f6d07d6b08137f8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.2" + bloc: + dependency: transitive + description: + name: bloc + sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + url: "https://pub.dev" + source: hosted + version: "8.1.2" characters: dependency: transitive description: @@ -33,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" easy_localization: dependency: "direct main" description: @@ -70,6 +86,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae + url: "https://pub.dev" + source: hosted + version: "8.1.3" flutter_localizations: dependency: transitive description: flutter @@ -80,6 +104,22 @@ packages: description: flutter source: sdk version: "0.0.0" + hive: + dependency: transitive + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + hydrated_bloc: + dependency: "direct main" + description: + name: hydrated_bloc + sha256: "24994e61f64904d911683cce1a31dc4ef611619da5253f1de2b7b8fc6f79a118" + url: "https://pub.dev" + source: hosted + version: "9.1.2" intl: dependency: transitive description: @@ -88,6 +128,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" + ionicons: + dependency: "direct main" + description: + name: ionicons + sha256: "5496bc65a16115ecf05b15b78f494ee4a8869504357668f0a11d689e970523cf" + url: "https://pub.dev" + source: hosted + version: "0.2.2" material_color_utilities: dependency: transitive description: @@ -104,6 +152,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -112,6 +168,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" path_provider_linux: dependency: transitive description: @@ -152,6 +232,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.6" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" shared_preferences: dependency: transitive description: @@ -213,6 +301,22 @@ packages: description: flutter source: sdk version: "0.0.99" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ed763febaf57b50e3d0117444bb53b007e89ee8b..46f75c51a02b5b0f3bf67c68d9fe644eaca0ed5e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A random application, for testing purpose only. publish_to: 'none' -version: 1.0.22+23 +version: 1.0.23+24 environment: sdk: '^3.0.0' @@ -13,9 +13,13 @@ dependencies: sdk: flutter easy_localization: ^3.0.1 + flutter_bloc: ^8.1.1 + path_provider: ^2.0.11 + hydrated_bloc: ^9.0.0 + ionicons: ^0.2.2 flutter: - uses-material-design: true + uses-material-design: false assets: - assets/menu/ - assets/translations/