Skip to content

Commit

Permalink
Fix system navigation bar behavior for Android APIs 26-28
Browse files Browse the repository at this point in the history
  • Loading branch information
AhmedLSayed9 committed Aug 10, 2023
1 parent 730910d commit ff9cb43
Show file tree
Hide file tree
Showing 17 changed files with 138 additions and 50 deletions.
6 changes: 6 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,8 @@ PODS:
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- device_info_plus (0.0.1):
- Flutter
- Firebase/Auth (10.10.0):
- Firebase/CoreOnly
- FirebaseAuth (~> 10.10.0)
Expand Down Expand Up @@ -852,6 +854,7 @@ PODS:
DEPENDENCIES:
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
Expand Down Expand Up @@ -902,6 +905,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/cloud_firestore/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
firebase_auth:
:path: ".symlinks/plugins/firebase_auth/ios"
firebase_core:
Expand Down Expand Up @@ -940,6 +945,7 @@ SPEC CHECKSUMS:
BoringSSL-GRPC: 3175b25143e648463a56daeaaa499c6cb86dad33
cloud_firestore: 4dd02c206e4b7e93ff081cdd0e7fa304112bf906
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
Firebase: facd334e557a979bd03a0b58d90fd56b52b8aba0
firebase_auth: 27829de4f51a06d305467eebc237157adc145571
firebase_core: 85b6664038311940ad60584eaabc73103c61f5de
Expand Down
5 changes: 4 additions & 1 deletion lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'core/core_features/locale/presentation/providers/current_app_locale_provider.dart';
import 'core/core_features/theme/presentation/providers/current_app_theme_provider.dart';
import 'core/core_features/theme/presentation/utils/app_theme.dart';
import 'core/presentation/extensions/device_info_extensions.dart';
import 'core/presentation/providers/device_info_providers.dart';
import 'core/presentation/routing/app_router.dart';
import 'core/presentation/routing/navigation_service.dart';
import 'core/presentation/utils/riverpod_framework.dart';
Expand All @@ -19,6 +21,7 @@ class MyApp extends HookConsumerWidget {
useOnPlatformBrightnessChange((previous, current) {
ref.read(platformBrightnessProvider.notifier).update((_) => current);
});
final isOldAndroid = ref.watch(androidDeviceInfoProvider).requireValue.isOldAndroid;
final themeMode = ref.watch(currentAppThemeModeProvider);
final locale = ref.watch(currentAppLocaleProvider);

Expand All @@ -36,7 +39,7 @@ class MyApp extends HookConsumerWidget {
title: 'Deliverzler',
debugShowCheckedModeBanner: false,
color: Theme.of(context).colorScheme.primary,
theme: themeMode.getThemeData(locale.fontFamily),
theme: themeMode.getThemeData(locale.fontFamily, isOldAndroid: isOldAndroid),
locale: Locale(locale.code),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import 'package:flutter/material.dart';

import 'custom_colors.dart';

part 'app_colors_light.dart';
part 'app_colors_dark.dart';

abstract class AppColors {
///Main
// The background color for major parts of the app (toolbars, tab bars, etc)
Expand All @@ -18,6 +21,7 @@ abstract class AppColors {
///Screen
Color get statusBarColor;
Color get systemNavBarColor;
Color get olderAndroidSystemNavBarColor;
Color get appBarBGColor;
Color get scaffoldBGColor;
Color get navBarColor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import 'package:flutter/material.dart';

import 'custom_colors.dart';
import 'app_colors.dart';
part of 'app_colors.dart';

class AppColorsDark implements AppColors {
@override
Expand All @@ -16,6 +13,8 @@ class AppColorsDark implements AppColors {
@override
Color get systemNavBarColor => Colors.transparent;
@override
Color get olderAndroidSystemNavBarColor => scaffoldBGColor;
@override
Color get appBarBGColor => scaffoldBGColor;
@override
Color get scaffoldBGColor => const Color(0xFF303030);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// ignore_for_file: overridden_fields

import 'package:flutter/material.dart';
import 'custom_colors.dart';
import 'app_colors.dart';
part of 'app_colors.dart';

class AppColorsLight implements AppColors {
@override
Expand All @@ -17,6 +15,8 @@ class AppColorsLight implements AppColors {
@override
Color get systemNavBarColor => Colors.transparent;
@override
Color get olderAndroidSystemNavBarColor => scaffoldBGColor;
@override
Color get appBarBGColor => scaffoldBGColor;
@override
Color get scaffoldBGColor => const Color(0xFFFAFAFA);
Expand Down
18 changes: 12 additions & 6 deletions lib/core/core_features/theme/presentation/utils/app_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import 'package:flutter/services.dart';
import '../../../../presentation/styles/styles.dart';
import 'custom_colors.dart';
import 'app_colors.dart';
import 'app_colors_dark.dart';
import 'app_colors_light.dart';

enum AppThemeMode {
light,
dark;
}

extension AppThemeModeX on AppThemeMode {
ThemeData getThemeData(String fontFamily) {
return AppTheme(themeMode: this).getThemeData(fontFamily);
ThemeData getThemeData(String fontFamily, {required bool isOldAndroid}) {
return AppTheme(themeMode: this, isOldAndroid: isOldAndroid).getThemeData(fontFamily);
}

ThemeData get _baseTheme {
Expand Down Expand Up @@ -58,10 +56,14 @@ extension AppThemeModeX on AppThemeMode {
}

class AppTheme {
AppTheme({required AppThemeMode themeMode}) : _themeMode = themeMode;
AppTheme({required AppThemeMode themeMode, required bool isOldAndroid})
: _isOldAndroid = isOldAndroid,
_themeMode = themeMode;

final AppThemeMode _themeMode;

final bool _isOldAndroid;

late final ThemeData _baseTheme = _themeMode._baseTheme;

late final AppColors _appColors = _themeMode._colorsPalette;
Expand All @@ -77,7 +79,11 @@ class AppTheme {
systemOverlayStyle: _themeMode._baseOverlayStyle.copyWith(
//For Android
statusBarColor: _appColors.statusBarColor,
systemNavigationBarColor: _appColors.systemNavBarColor,
// Not using transparent color for old android versions to avoid hidden navigation bar icons:
// https://github.com/flutter/flutter/issues/105716
systemNavigationBarColor:
_isOldAndroid ? _appColors.olderAndroidSystemNavBarColor : _appColors.systemNavBarColor,
systemNavigationBarDividerColor: _appColors.systemNavBarColor,
),
backgroundColor: _appColors.appBarBGColor,
elevation: Sizes.appBarElevation,
Expand Down
9 changes: 9 additions & 0 deletions lib/core/presentation/extensions/device_info_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:device_info_plus/device_info_plus.dart';

import '../utils/fp_framework.dart';

extension AndroidDeviceInfoX on Option<AndroidDeviceInfo> {
bool get isOldAndroid {
return match(() => false, (info) => info.version.sdkInt < 29);
}
}
14 changes: 12 additions & 2 deletions lib/core/presentation/helpers/theme_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';

import '../../core_features/theme/presentation/utils/app_colors.dart';
import '../../core_features/theme/presentation/utils/app_theme.dart';

bool isDarkMode([Brightness? platformBrightness]) {
Expand All @@ -19,18 +20,27 @@ AppThemeMode getSystemTheme([Brightness? platformBrightness]) {
SystemUiOverlayStyle getFullScreenOverlayStyle(
BuildContext context, {
required bool darkOverlays,
required bool isOldAndroid,
}) {
final systemNavBarColor = switch (isOldAndroid) {
true when darkOverlays => AppColorsLight().olderAndroidSystemNavBarColor,
true when !darkOverlays => AppColorsDark().olderAndroidSystemNavBarColor,
_ => Colors.transparent,
};

return darkOverlays
? SystemUiOverlayStyle.dark.copyWith(
//For Android
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarColor: systemNavBarColor,
systemNavigationBarDividerColor: systemNavBarColor,
systemNavigationBarIconBrightness: Brightness.dark,
)
: SystemUiOverlayStyle.light.copyWith(
//For Android
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarColor: systemNavBarColor,
systemNavigationBarDividerColor: systemNavBarColor,
systemNavigationBarIconBrightness: Brightness.light,
);
}
17 changes: 17 additions & 0 deletions lib/core/presentation/providers/device_info_providers.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'dart:io';

import 'package:device_info_plus/device_info_plus.dart';

import '../utils/fp_framework.dart';
import '../utils/riverpod_framework.dart';

part 'device_info_providers.g.dart';

@Riverpod(keepAlive: true)
FutureOr<Option<AndroidDeviceInfo>> androidDeviceInfo(AndroidDeviceInfoRef ref) async {
ref.onDispose(() {
print('dispooose');
});
if (Platform.isAndroid) return await DeviceInfoPlugin().androidInfo.then(Some.new);
return const None();
}
4 changes: 4 additions & 0 deletions lib/core/presentation/screens/full_screen_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import 'package:flutter/material.dart';

import '../../core_features/theme/presentation/providers/current_app_theme_provider.dart';
import '../../core_features/theme/presentation/utils/app_theme.dart';
import '../extensions/device_info_extensions.dart';
import '../helpers/theme_helper.dart';
import '../providers/device_info_providers.dart';
import '../utils/riverpod_framework.dart';
import '../widgets/status_bar_spacer.dart';

Expand All @@ -23,13 +25,15 @@ class FullScreenScaffold extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentTheme = ref.watch(currentAppThemeModeProvider);
final isOldAndroid = ref.watch(androidDeviceInfoProvider).requireValue.isOldAndroid;

return Scaffold(
appBar: hasStatusBarSpace ? StatusBarSpacer(statusBarColor: statusBarColor) : null,
body: AnnotatedRegion(
value: getFullScreenOverlayStyle(
context,
darkOverlays: darkOverlays ?? currentTheme == AppThemeMode.light,
isOldAndroid: isOldAndroid,
),
child: body,
),
Expand Down
8 changes: 7 additions & 1 deletion lib/core/presentation/services/main_initializer.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
part of '../../../main.dart';

Future<void> _mainInitializer() async {
Future<void> _mainInitializer(ProviderContainer container) async {
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
_setupLogger();
await _initFirebase();

// Caching the android device info to be used synchronously at AppTheme to setup the navigation bar
// behavior for older Android versions without flickering (of the navigation bar) when app starts.
await container.read(androidDeviceInfoProvider.future).silenceError();

// This Prevent closing native splash screen until we finish warming-up custom splash images.
// App layout will be built but not displayed.
// Also, theme/locale will be loaded (async) during this time (A default theme/locale is set initially).
widgetsBinding.deferFirstFrame();
widgetsBinding.addPostFrameCallback((_) async {
// Run any function you want to wait for before showing app layout.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import 'package:flutter/material.dart';

import '../../../../core_features/theme/presentation/providers/current_app_theme_provider.dart';
import '../../../../core_features/theme/presentation/utils/app_theme.dart';
import '../../../extensions/device_info_extensions.dart';
import '../../../helpers/theme_helper.dart';
import '../../../providers/device_info_providers.dart';
import '../../../utils/riverpod_framework.dart';
import 'platform_scaffold.dart';

Expand All @@ -19,13 +21,15 @@ class FullScreenPlatformScaffold extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentTheme = ref.watch(currentAppThemeModeProvider);
final isOldAndroid = ref.watch(androidDeviceInfoProvider).requireValue.isOldAndroid;

return PlatformScaffold(
hasStatusBarSpace: false,
body: AnnotatedRegion(
value: getFullScreenOverlayStyle(
context,
darkOverlays: darkOverlays ?? currentTheme == AppThemeMode.light,
isOldAndroid: isOldAndroid,
),
child: body,
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import 'package:flutter/material.dart';

import '../../../../../core/core_features/theme/presentation/providers/current_app_theme_provider.dart';
import '../../../../../core/core_features/theme/presentation/utils/app_theme.dart';
import '../../../../../core/presentation/helpers/localization_helper.dart';
import '../../../../../core/presentation/helpers/theme_helper.dart';
import '../../../../../core/presentation/routing/app_router.dart';
import '../../../../../core/presentation/screens/nested_screen_scaffold.dart';
import '../../../../../core/presentation/services/local_notfication_service/show_local_notification_provider.dart';
Expand All @@ -29,7 +26,6 @@ class MapScreenCompact extends HookConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final currentTheme = ref.watch(currentAppThemeModeProvider);
final locationAsync = ref.watch(locationStreamProvider);

useEffect(
Expand Down Expand Up @@ -67,34 +63,28 @@ class MapScreenCompact extends HookConsumerWidget {
},
);

return AnnotatedRegion(
value: getFullScreenOverlayStyle(
context,
darkOverlays: currentTheme == AppThemeMode.light,
),
child: NestedScreenScaffold(
body: locationAsync.when(
skipLoadingOnReload: true,
skipLoadingOnRefresh: !locationAsync.hasError,
loading: () => TitledLoadingIndicator(message: tr(context).determine_location),
error: (error, st) => RetryAgainComponent(
description: (error as LocationError).getErrorText(context),
onPressed: () {
ref.invalidate(locationStreamProvider);
},
),
data: (_) => const Stack(
alignment: Alignment.topCenter,
fit: StackFit.expand,
children: [
GoogleMapComponent(),
MapDirectionsInfoComponent(),
MapPhoneCallComponent(),
MapConfirmButtonComponent(),
MapFloatingSearchBar(),
MapFloatingActionButton(),
],
),
return NestedScreenScaffold(
body: locationAsync.when(
skipLoadingOnReload: true,
skipLoadingOnRefresh: !locationAsync.hasError,
loading: () => TitledLoadingIndicator(message: tr(context).determine_location),
error: (error, st) => RetryAgainComponent(
description: (error as LocationError).getErrorText(context),
onPressed: () {
ref.invalidate(locationStreamProvider);
},
),
data: (_) => const Stack(
alignment: Alignment.topCenter,
fit: StackFit.expand,
children: [
GoogleMapComponent(),
MapDirectionsInfoComponent(),
MapPhoneCallComponent(),
MapConfirmButtonComponent(),
MapFloatingSearchBar(),
MapFloatingActionButton(),
],
),
),
);
Expand Down
5 changes: 4 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:logging/logging.dart';

import 'app.dart';
import 'core/presentation/extensions/future_extensions.dart';
import 'core/presentation/providers/device_info_providers.dart';
import 'core/presentation/providers/provider_observers.dart';
import 'core/presentation/utils/riverpod_framework.dart';
import 'firebase_options.dart';
Expand All @@ -17,9 +18,11 @@ import 'gen/my_assets.dart';
part 'core/presentation/services/main_initializer.dart';

void main() async {
await _mainInitializer();
final container = ProviderContainer();
await _mainInitializer(container);
runApp(
ProviderScope(
parent: container,
observers: [ProviderLogger(), ProviderCrashlytics()],
child: const MyApp(),
),
Expand Down
Loading

0 comments on commit ff9cb43

Please sign in to comment.