From b4145dc5c8f6e65182cade31f209f2aa7dfaf1be Mon Sep 17 00:00:00 2001 From: Inex Code Date: Tue, 17 May 2022 01:41:00 +0300 Subject: [PATCH] First steps to move to Material You --- lib/main.dart | 7 +- lib/theming/factory/app_theme_factory.dart | 19 ++-- .../components/brand_button/FilledButton.dart | 26 +++++ .../components/brand_button/brand_button.dart | 100 ++++-------------- .../brand_hero_screen/brand_hero_screen.dart | 23 ++-- .../brand_tab_bar/brand_tab_bar.dart | 12 +-- .../pre_styled_buttons/flashFab.dart | 93 ++++++++++++++++ lib/ui/pages/rootRoute.dart | 5 +- .../setup/recovering/recovery_domain.dart | 9 +- 9 files changed, 177 insertions(+), 117 deletions(-) create mode 100644 lib/ui/components/brand_button/FilledButton.dart create mode 100644 lib/ui/components/pre_styled_buttons/flashFab.dart diff --git a/lib/main.dart b/lib/main.dart index 716df28b..3f085bfc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,7 +13,6 @@ import 'package:timezone/data/latest.dart' as tz; import 'config/bloc_config.dart'; import 'config/bloc_observer.dart'; -import 'config/brand_theme.dart'; import 'config/get_it_config.dart'; import 'config/localization.dart'; import 'logic/cubit/app_settings/app_settings_cubit.dart'; @@ -45,7 +44,8 @@ void main() async { ); BlocOverrides.runZoned( - () => runApp(Localization(child: MyApp( + () => runApp(Localization( + child: MyApp( lightThemeData: lightThemeData, darkThemeData: darkThemeData, ))), @@ -81,7 +81,8 @@ class MyApp extends StatelessWidget { title: 'SelfPrivacy', theme: lightThemeData, darkTheme: darkThemeData, - themeMode: appSettings.isDarkModeOn ? ThemeMode.dark : ThemeMode.light, + themeMode: + appSettings.isDarkModeOn ? ThemeMode.dark : ThemeMode.light, home: appSettings.isOnbordingShowing ? OnboardingPage(nextPage: InitializingPage()) : RootPage(), diff --git a/lib/theming/factory/app_theme_factory.dart b/lib/theming/factory/app_theme_factory.dart index 82407dfb..6d0ce1de 100644 --- a/lib/theming/factory/app_theme_factory.dart +++ b/lib/theming/factory/app_theme_factory.dart @@ -28,13 +28,18 @@ abstract class AppThemeFactory { if (Platform.isLinux) { GtkThemeData themeData = await GtkThemeData.initialize(); + final isGtkDark = + Color(themeData.theme_base_color).computeLuminance() < 0.5; + final isInverseNeeded = isGtkDark != isDark; gtkColorsScheme = ColorScheme.fromSeed( seedColor: Color(themeData.theme_selected_bg_color), - brightness: Color(themeData.theme_base_color).computeLuminance() > 0.5 - ? Brightness.light - : Brightness.dark, - background: Color(themeData.theme_bg_color), - surface: Color(themeData.theme_base_color), + brightness: brightness, + background: isInverseNeeded + ? Color(themeData.theme_base_color) + : Color(themeData.theme_bg_color), + surface: isInverseNeeded + ? Color(themeData.theme_bg_color) + : Color(themeData.theme_base_color), ); } @@ -46,7 +51,8 @@ abstract class AppThemeFactory { brightness: brightness, ); - final colorScheme = dynamicColorsScheme ?? gtkColorsScheme ?? fallbackColorScheme; + final colorScheme = + dynamicColorsScheme ?? gtkColorsScheme ?? fallbackColorScheme; final appTypography = Typography.material2021(); @@ -55,6 +61,7 @@ abstract class AppThemeFactory { brightness: colorScheme.brightness, typography: appTypography, useMaterial3: true, + scaffoldBackgroundColor: colorScheme.background, appBarTheme: AppBarTheme( elevation: 0, backgroundColor: colorScheme.primary, diff --git a/lib/ui/components/brand_button/FilledButton.dart b/lib/ui/components/brand_button/FilledButton.dart new file mode 100644 index 00000000..0ac34a42 --- /dev/null +++ b/lib/ui/components/brand_button/FilledButton.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class FilledButton extends StatelessWidget { + const FilledButton({ + Key? key, + this.onPressed, + this.title, + this.child, + }) : super(key: key); + + final VoidCallback? onPressed; + final String? title; + final Widget? child; + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: onPressed, + child: child ?? Text(title ?? ''), + style: ElevatedButton.styleFrom( + onPrimary: Theme.of(context).colorScheme.onPrimary, + primary: Theme.of(context).colorScheme.primary, + ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)), + ); + } +} diff --git a/lib/ui/components/brand_button/brand_button.dart b/lib/ui/components/brand_button/brand_button.dart index 86020121..b09b0442 100644 --- a/lib/ui/components/brand_button/brand_button.dart +++ b/lib/ui/components/brand_button/brand_button.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/ui/components/brand_button/FilledButton.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; enum BrandButtonTypes { rised, text, iconText } @@ -13,11 +13,17 @@ class BrandButton { }) { assert(text == null || child == null, 'required title or child'); assert(text != null || child != null, 'required title or child'); - return _RisedButton( - key: key, - title: text, - onPressed: onPressed, - child: child, + return ConstrainedBox( + constraints: BoxConstraints( + minHeight: 48, + minWidth: double.infinity, + ), + child: FilledButton( + key: key, + title: text, + onPressed: onPressed, + child: child, + ), ); } @@ -26,10 +32,12 @@ class BrandButton { required VoidCallback onPressed, required String title, }) => - _TextButton( - key: key, - title: title, - onPressed: onPressed, + ConstrainedBox( + constraints: BoxConstraints( + minHeight: 48, + minWidth: double.infinity, + ), + child: TextButton(onPressed: onPressed, child: Text(title)), ); static emptyWithIconText({ @@ -46,78 +54,6 @@ class BrandButton { ); } -class _RisedButton extends StatelessWidget { - const _RisedButton({ - Key? key, - this.onPressed, - this.title, - this.child, - }) : super(key: key); - - final VoidCallback? onPressed; - final String? title; - final Widget? child; - - @override - Widget build(BuildContext context) { - return ClipRRect( - borderRadius: BorderRadius.circular(24), - child: ColoredBox( - color: onPressed == null - ? BrandColors.gray2 - : Theme.of(context).primaryColor, - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: onPressed, - child: Container( - height: 48, - width: double.infinity, - alignment: Alignment.center, - padding: EdgeInsets.all(12), - child: child ?? BrandText.buttonTitleText(title), - ), - ), - ), - ), - ); - } -} - -class _TextButton extends StatelessWidget { - const _TextButton({ - Key? key, - this.onPressed, - this.title, - }) : super(key: key); - - final VoidCallback? onPressed; - final String? title; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onPressed, - behavior: HitTestBehavior.opaque, - child: Container( - height: 48, - width: double.infinity, - alignment: Alignment.center, - padding: EdgeInsets.all(12), - child: Text( - title!, - style: TextStyle( - color: BrandColors.blue, - fontSize: 16, - fontWeight: FontWeight.bold, - height: 1.5, - ), - ), - ), - ); - } -} - class _IconTextButton extends StatelessWidget { const _IconTextButton({Key? key, this.onPressed, this.title, this.icon}) : super(key: key); diff --git a/lib/ui/components/brand_hero_screen/brand_hero_screen.dart b/lib/ui/components/brand_hero_screen/brand_hero_screen.dart index 934c952f..efa75515 100644 --- a/lib/ui/components/brand_hero_screen/brand_hero_screen.dart +++ b/lib/ui/components/brand_hero_screen/brand_hero_screen.dart @@ -37,20 +37,27 @@ class BrandHeroScreen extends StatelessWidget { padding: EdgeInsets.all(16.0), children: [ if (heroIcon != null) - Icon( - heroIcon, - size: 48.0, + Container( + child: Icon( + heroIcon, + size: 48.0, + ), + alignment: Alignment.bottomLeft, ), - SizedBox(height: 16.0), + SizedBox(height: 8.0), if (heroTitle != null) - Text(heroTitle!, - style: Theme.of(context).textTheme.headline2, - textAlign: TextAlign.center), + Text( + heroTitle!, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Colors.black, + ), + textAlign: TextAlign.start, + ), SizedBox(height: 8.0), if (heroSubtitle != null) Text(heroSubtitle!, style: Theme.of(context).textTheme.bodyMedium, - textAlign: TextAlign.center), + textAlign: TextAlign.start), SizedBox(height: 16.0), ...children, ], diff --git a/lib/ui/components/brand_tab_bar/brand_tab_bar.dart b/lib/ui/components/brand_tab_bar/brand_tab_bar.dart index 3b0b0dac..3975ee25 100644 --- a/lib/ui/components/brand_tab_bar/brand_tab_bar.dart +++ b/lib/ui/components/brand_tab_bar/brand_tab_bar.dart @@ -1,10 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -final _kBottomTabBarHeight = 51; - class BrandTabBar extends StatefulWidget { BrandTabBar({Key? key, this.controller}) : super(key: key); @@ -43,24 +40,17 @@ class _BrandTabBarState extends State { _getIconButton('basis.providers'.tr(), BrandIcons.server, 0), _getIconButton('basis.services'.tr(), BrandIcons.box, 1), _getIconButton('basis.users'.tr(), BrandIcons.users, 2), - _getIconButton('basis.more'.tr(), BrandIcons.menu, 3), + _getIconButton('basis.more'.tr(), Icons.menu_rounded, 3), ], onDestinationSelected: (index) { widget.controller!.animateTo(index); }, selectedIndex: currentIndex ?? 0, labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected, - ); } _getIconButton(String label, IconData iconData, int index) { - var activeColor = Theme.of(context).brightness == Brightness.dark - ? BrandColors.white - : BrandColors.black; - - var isActive = currentIndex == index; - var color = isActive ? activeColor : BrandColors.inactive; return NavigationDestination( icon: Icon(iconData), label: label, diff --git a/lib/ui/components/pre_styled_buttons/flashFab.dart b/lib/ui/components/pre_styled_buttons/flashFab.dart new file mode 100644 index 00000000..0af5bcab --- /dev/null +++ b/lib/ui/components/pre_styled_buttons/flashFab.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:ionicons/ionicons.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; +import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; + +class BrandFab extends StatefulWidget { + BrandFab({Key? key}) : super(key: key); + + @override + _BrandFabState createState() => _BrandFabState(); +} + +class _BrandFabState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _colorTween; + + @override + void initState() { + _animationController = + AnimationController(vsync: this, duration: Duration(milliseconds: 800)); + _colorTween = ColorTween( + begin: BrandColors.black, + end: BrandColors.primary, + ).animate(_animationController); + + super.initState(); + WidgetsBinding.instance.addPostFrameCallback(_afterLayout); + } + + void _afterLayout(_) { + if (Theme.of(context).brightness == Brightness.dark) { + setState(() { + _colorTween = ColorTween( + begin: BrandColors.white, + end: BrandColors.primary, + ).animate(_animationController); + }); + } + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + bool wasPrevStateIsEmpty = true; + + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + if (wasPrevStateIsEmpty && state is! JobsStateEmpty) { + wasPrevStateIsEmpty = false; + _animationController.forward(); + } else if (!wasPrevStateIsEmpty && state is JobsStateEmpty) { + wasPrevStateIsEmpty = true; + + _animationController.reverse(); + } + }, + child: FloatingActionButton( + onPressed: () { + showBrandBottomSheet( + context: context, + builder: (context) => BrandBottomSheet( + isExpended: true, + child: JobsContent(), + ), + ); + }, + child: AnimatedBuilder( + animation: _colorTween, + builder: (context, child) { + var v = _animationController.value; + var icon = v > 0.5 ? Ionicons.flash : Ionicons.flash_outline; + return Transform.scale( + scale: 1 + (v < 0.5 ? v : 1 - v) * 2, + child: Icon( + icon, + color: _colorTween.value, + ), + ); + }), + ), + ); + } +} diff --git a/lib/ui/pages/rootRoute.dart b/lib/ui/pages/rootRoute.dart index 24865688..eabb0a10 100644 --- a/lib/ui/pages/rootRoute.dart +++ b/lib/ui/pages/rootRoute.dart @@ -1,14 +1,14 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; -import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart'; import 'package:selfprivacy/ui/pages/more/more.dart'; import 'package:selfprivacy/ui/pages/providers/providers.dart'; import 'package:selfprivacy/ui/pages/services/services.dart'; import 'package:selfprivacy/ui/pages/users/users.dart'; +import '../components/pre_styled_buttons/flashFab.dart'; + class RootPage extends StatefulWidget { const RootPage({Key? key}) : super(key: key); @@ -53,6 +53,7 @@ class _RootPageState extends State bottomNavigationBar: BrandTabBar( controller: tabController, ), + floatingActionButton: BrandFab(), ), ); } diff --git a/lib/ui/pages/setup/recovering/recovery_domain.dart b/lib/ui/pages/setup/recovering/recovery_domain.dart index 8ba452d7..151b5314 100644 --- a/lib/ui/pages/setup/recovering/recovery_domain.dart +++ b/lib/ui/pages/setup/recovering/recovery_domain.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; +import 'package:selfprivacy/ui/components/brand_button/FilledButton.dart'; import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; class RecoveryDomain extends StatelessWidget { @@ -29,18 +29,17 @@ class RecoveryDomain extends StatelessWidget { ), ), SizedBox(height: 16), - BrandButton.rised( + FilledButton( + title: "more.continue".tr(), onPressed: formCubitState.isSubmitting ? null : () => context.read().trySubmit(), - text: "more.continue".tr(), - ), + ) ], heroTitle: "recovering.recovery_main_header".tr(), heroSubtitle: "recovering.domain_recovery_description".tr(), hasBackButton: true, hasFlashButton: false, - heroIcon: Icons.link, ); }), );