First steps to move to Material You

pull/90/head
Inex Code 2022-05-17 01:41:00 +03:00
parent 8b5bf24f3a
commit b4145dc5c8
9 changed files with 177 additions and 117 deletions

View File

@ -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(),

View File

@ -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,

View File

@ -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)),
);
}
}

View File

@ -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);

View File

@ -37,20 +37,27 @@ class BrandHeroScreen extends StatelessWidget {
padding: EdgeInsets.all(16.0),
children: <Widget>[
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,
],

View File

@ -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<BrandTabBar> {
_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,

View File

@ -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<BrandFab>
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<JobsCubit, JobsState>(
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,
),
);
}),
),
);
}
}

View File

@ -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<RootPage>
bottomNavigationBar: BrandTabBar(
controller: tabController,
),
floatingActionButton: BrandFab(),
),
);
}

View File

@ -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<RecoveryDomainFormCubit>().trySubmit(),
text: "more.continue".tr(),
),
)
],
heroTitle: "recovering.recovery_main_header".tr(),
heroSubtitle: "recovering.domain_recovery_description".tr(),
hasBackButton: true,
hasFlashButton: false,
heroIcon: Icons.link,
);
}),
);