Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 38 additions & 28 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:edumfa_authenticator/utils/globals.dart';
import 'package:edumfa_authenticator/utils/logger.dart';
Expand Down Expand Up @@ -78,8 +79,12 @@ class EduMFAAuthenticator extends ConsumerWidget {
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(appConstraintsProvider.notifier).state = constraints;
});
return DynamicColorBuilder(
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
return PlatformProvider(
settings: PlatformSettingsData(
iosUsesMaterialWidgets: true
),
builder: (context) => DynamicColorBuilder(
builder: (lightDynamic, darkDynamic) {
ThemeData lightTheme = ThemeData(colorScheme: generateColorScheme(
lightDynamic?.primary,
brandColor
Expand All @@ -100,34 +105,39 @@ class EduMFAAuthenticator extends ConsumerWidget {
lightTheme = lightTheme.copyWith(navigationRailTheme: navigationRailThemeData);
darkTheme = darkTheme.copyWith(navigationRailTheme: navigationRailThemeData);

return MaterialApp(
debugShowCheckedModeBanner: true,
navigatorKey: globalNavigatorKey,
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate
],
supportedLocales: S.delegate.supportedLocales,
title: appName,
theme: lightTheme,
darkTheme: darkTheme,
scaffoldMessengerKey: globalSnackbarKey, // <= this
return PlatformTheme(
themeMode: EasyDynamicTheme.of(context).themeMode,
initialRoute: SplashScreen.routeName,
routes: {
AboutSettingsView.routeName: (context) => const AboutSettingsView(),
AppearanceSettingsView.routeName: (context) => const AppearanceSettingsView(),
LicenseView.routeName: (context) => const LicenseView(),
MainView.routeName: (context) => const MainView(),
OnboardingView.routeName: (context) => const OnboardingView(),
PushTokenSettingsView.routeName: (context) => const PushTokenSettingsView(),
SettingsView.routeName: (context) => const SettingsView(),
SplashScreen.routeName: (context) => const SplashScreen(),
},
materialLightTheme: lightTheme,
materialDarkTheme: darkTheme,
builder: (context) => PlatformApp(
debugShowCheckedModeBanner: true,
navigatorKey: globalNavigatorKey,
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate
],
supportedLocales: S.delegate.supportedLocales,
title: appName,
initialRoute: SplashScreen.routeName,
routes: {
AboutSettingsView.routeName: (context) => const AboutSettingsView(),
AppearanceSettingsView.routeName: (context) => const AppearanceSettingsView(),
LicenseView.routeName: (context) => const LicenseView(),
MainView.routeName: (context) => const MainView(),
OnboardingView.routeName: (context) => const OnboardingView(),
PushTokenSettingsView.routeName: (context) => const PushTokenSettingsView(),
SettingsView.routeName: (context) => const SettingsView(),
SplashScreen.routeName: (context) => const SplashScreen(),
},
material: (_, __) => MaterialAppData(
scaffoldMessengerKey: globalSnackbarKey
),
),
);
}
},
),
);
});
}
Expand Down
70 changes: 50 additions & 20 deletions lib/views/tokens_view/tokens_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import 'package:edumfa_authenticator/views/tokens_view/tokens_view_widgets/token
import 'package:edumfa_authenticator/views/view_interface.dart';
import 'package:edumfa_authenticator/widgets/conditional_floating_action_button.dart';
import 'package:edumfa_authenticator/widgets/status_bar.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:haptic_feedback/haptic_feedback.dart';

Expand All @@ -28,21 +30,39 @@ class TokensViewState extends ConsumerState<TokensView> {

@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
floatingActionButton: !isTablet(context)
? ConditionalFloatingActionButton(
isExtended: ref.watch(tokenProvider).tokens.isEmpty,
label: S.of(context).addToken,
icon: Icons.add,
return PlatformScaffold(
material: (_, __) => MaterialScaffoldData(
resizeToAvoidBottomInset: false,
floatingActionButton: !isTablet(context)
? ConditionalFloatingActionButton(
isExtended: ref.watch(tokenProvider).tokens.isEmpty,
label: S.of(context).addToken,
icon: Icons.add,
onPressed: () async => await showAddTokenSheet(context),
) : null,
appBar: const PreferredSize(
preferredSize: Size.fromHeight(70),
child: SafeArea(
child: Padding(
padding: EdgeInsets.only(left: 20, right: 20, top: 10),
child: TokenSearchBar(),
),
),
),
),
appBar: PlatformAppBar(
cupertino: (context, platform) => CupertinoNavigationBarData(
title: Text(S.of(context).tokens),
trailing: CupertinoButton(
child: const Icon(CupertinoIcons.add),
onPressed: () async => await showAddTokenSheet(context),
) : null,
appBar: const PreferredSize(
preferredSize: Size.fromHeight(70),
child: SafeArea(
child: Padding(
padding: EdgeInsets.only(left: 20, right: 20, top: 10),
child: TokenSearchBar(),
),
bottom: const PreferredSize(
preferredSize: Size.fromHeight(35.0 + 8 * 2),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: TokenSearchBar(),
)
),
),
),
Expand All @@ -57,12 +77,22 @@ class TokensViewState extends ConsumerState<TokensView> {
Future<void> showAddTokenSheet(BuildContext context) async {
await Haptics.vibrate(HapticsType.rigid);
if (!context.mounted) return;
showModalBottomSheet(
context: context,
isScrollControlled: true,
showDragHandle: true,
builder: (context) => const AddTokenSheetWidget(),
);
if (isMaterial(context)) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
showDragHandle: true,
builder: (context) => const AddTokenSheetWidget(),
);
} else {
showCupertinoSheet(
context: context,
pageBuilder: (_) => const CupertinoPageScaffold(
child: AddTokenSheetWidget(),
),
);
}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:edumfa_authenticator/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mobile_scanner/mobile_scanner.dart';

Expand All @@ -14,7 +16,15 @@ class UploadQrCodeButton extends StatelessWidget {
@override
Widget build(BuildContext context) => SizedBox(
width: double.infinity,
child: FilledButton.icon(
child: PlatformElevatedButton(
material: (_, platform) => MaterialElevatedButtonData(
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
onPressed: () async {
final XFile? image = await ImagePicker().pickImage(
source: ImageSource.gallery,
Expand All @@ -26,14 +36,18 @@ class UploadQrCodeButton extends StatelessWidget {
);
handleBarcodes(barcodes);
},
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10.0,
children: [
PlatformWidget(
material: (_, __) => const Icon(Icons.upload_file),
cupertino: (_, __) => const Icon(CupertinoIcons.arrow_up_doc),
),
Text(S.of(context).uploadQrCodeButton)
],
),
icon: const Icon(Icons.upload_file),
label: Text(S.of(context).uploadQrCodeButton),

),
);
}
127 changes: 75 additions & 52 deletions lib/views/tokens_view/tokens_view_widgets/token_search_bar.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'package:edumfa_authenticator/generated/l10n.dart';
import 'package:edumfa_authenticator/model/states/token_filter.dart';
import 'package:edumfa_authenticator/utils/riverpod_providers.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:haptic_feedback/haptic_feedback.dart';
Expand Down Expand Up @@ -31,60 +33,81 @@ class _TokenSearchBarState extends ConsumerState<TokenSearchBar> {
@override
Widget build(BuildContext context) {
var tokenFilter = ref.read(tokenFilterProvider.notifier).state;
return SearchBar(
key: _searchBarKey,
controller: _searchController,
hintText: S.of(context).search,
textInputAction: TextInputAction.search,
focusNode: _searchFocusNode,
onTapOutside: (event) {
final RenderBox box = _searchBarKey.currentContext?.findRenderObject() as RenderBox;
final Rect rect = box.localToGlobal(Offset.zero) & box.size;
if (!rect.contains(event.position)) {
_searchFocusNode.unfocus();
}
},
onTap: () async => await Haptics.vibrate(HapticsType.soft),
onChanged: (value) {
ref.read(tokenFilterProvider.notifier).state = _searchController.text.isEmpty
? null
: TokenFilter(searchQuery: value);
setState(() {});
},
leading: SizedBox(
width: 50,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: _searchFocusNode.hasFocus || tokenFilter != null
? const Center(child: Icon(Icons.search))
: SvgPicture.asset(
'res/logo/app_icon.svg',
width: 32,
height: 32,
colorFilter: ColorFilter.mode(
Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white,
BlendMode.srcIn
return PlatformWidget(
material:
(_, __) => SearchBar(
key: _searchBarKey,
controller: _searchController,
hintText: S.of(context).search,
textInputAction: TextInputAction.search,
focusNode: _searchFocusNode,
onTapOutside: (event) {
final RenderBox box =
_searchBarKey.currentContext?.findRenderObject() as RenderBox;
final Rect rect = box.localToGlobal(Offset.zero) & box.size;
if (!rect.contains(event.position)) {
_searchFocusNode.unfocus();
}
},
onTap: () async => await Haptics.vibrate(HapticsType.soft),
onChanged: (value) {
ref.read(tokenFilterProvider.notifier).state =
_searchController.text.isEmpty
? null
: TokenFilter(searchQuery: value);
setState(() {});
},
leading: SizedBox(
width: 50,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child:
_searchFocusNode.hasFocus || tokenFilter != null
? const Center(child: Icon(Icons.search))
: SvgPicture.asset(
'res/logo/app_icon.svg',
width: 32,
height: 32,
colorFilter: ColorFilter.mode(
Theme.of(context).brightness == Brightness.light
? Colors.black
: Colors.white,
BlendMode.srcIn,
),
),
),
),
trailing: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child:
tokenFilter?.searchQuery.isNotEmpty ?? false
? IconButton(
onPressed: () {
_searchController.clear();
ref.read(tokenFilterProvider.notifier).state = null;
setState(() {});
},
icon: const Icon(Icons.close),
)
: const SizedBox(),
),
],
constraints: const BoxConstraints(minHeight: double.infinity),
elevation: WidgetStateProperty.all<double>(0),
),
// TODO: We need to find a way to unfocus it when tapped outside
cupertino:
(_, __) => CupertinoSearchTextField(
controller: _searchController,
onChanged: (value) {
ref.read(tokenFilterProvider.notifier).state =
_searchController.text.isEmpty
? null
: TokenFilter(searchQuery: value);
setState(() {});
},
),
),
),
trailing: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: tokenFilter?.searchQuery.isNotEmpty ?? false
? IconButton(
onPressed: () {
_searchController.clear();
ref.read(tokenFilterProvider.notifier).state = null;
setState(() {});
},
icon: const Icon(Icons.close)
)
: const SizedBox()
)
],
constraints: const BoxConstraints(minHeight: double.infinity),
elevation: WidgetStateProperty.all<double>(0),
);
}

Expand Down
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0"
flutter_platform_widgets:
dependency: "direct main"
description:
name: flutter_platform_widgets
sha256: ba28f1a1ee7e46e2b7315405868c2d7126ba67e74e83e9a80538f8d5b5df7b21
url: "https://pub.dev"
source: hosted
version: "8.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
Expand Down
Loading