Add widget for empty state and coach mark
This commit is contained in:
parent
4d88503c2b
commit
73c130ce7e
4 changed files with 140 additions and 110 deletions
|
@ -7,7 +7,7 @@ class PreferenceService {
|
||||||
|
|
||||||
late final SharedPreferences _prefs;
|
late final SharedPreferences _prefs;
|
||||||
|
|
||||||
static const kHasShownCoachMarkKey = "has_shown_coach_mark";
|
static const kHasShownCoachMarkKey = "has_shown_coach_markx";
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
_prefs = await SharedPreferences.getInstance();
|
_prefs = await SharedPreferences.getInstance();
|
||||||
|
|
71
lib/ui/home/coach_mark_widget.dart
Normal file
71
lib/ui/home/coach_mark_widget.dart
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:ente_auth/core/event_bus.dart';
|
||||||
|
import 'package:ente_auth/events/codes_updated_event.dart';
|
||||||
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
|
import 'package:ente_auth/services/preference_service.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CoachMarkWidget extends StatelessWidget {
|
||||||
|
const CoachMarkWidget({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
await PreferenceService.instance.setHasShownCoachMark(true);
|
||||||
|
Bus.instance.fire(CodesUpdatedEvent());
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
color: Theme.of(context).colorScheme.background.withOpacity(0.1),
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.swipe_left,
|
||||||
|
size: 42,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
l10n.swipeHint,
|
||||||
|
style: Theme.of(context).textTheme.headline6,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 36,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 160,
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await PreferenceService.instance
|
||||||
|
.setHasShownCoachMark(true);
|
||||||
|
Bus.instance.fire(CodesUpdatedEvent());
|
||||||
|
},
|
||||||
|
child: Text(l10n.ok),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
61
lib/ui/home/home_empty_state.dart
Normal file
61
lib/ui/home/home_empty_state.dart
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class HomeEmptyStateWidget extends StatelessWidget {
|
||||||
|
final VoidCallback? onScanTap;
|
||||||
|
final VoidCallback? onManuallySetupTap;
|
||||||
|
|
||||||
|
const HomeEmptyStateWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.onScanTap,
|
||||||
|
required this.onManuallySetupTap,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
return Center(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints.tightFor(height: 800, width: 450),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
"assets/wallet-front-gradient.png",
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
l10n.setupFirstAccount,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.headline4,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 64),
|
||||||
|
SizedBox(
|
||||||
|
width: 400,
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: onScanTap,
|
||||||
|
child: Text(l10n.importScanQrCode),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 18),
|
||||||
|
SizedBox(
|
||||||
|
width: 400,
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: onManuallySetupTap,
|
||||||
|
child: Text(l10n.importEnterSetupKey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:ente_auth/core/event_bus.dart';
|
import 'package:ente_auth/core/event_bus.dart';
|
||||||
import 'package:ente_auth/ente_theme_data.dart';
|
import 'package:ente_auth/ente_theme_data.dart';
|
||||||
|
@ -15,6 +14,8 @@ import 'package:ente_auth/store/code_store.dart';
|
||||||
import 'package:ente_auth/ui/account/logout_dialog.dart';
|
import 'package:ente_auth/ui/account/logout_dialog.dart';
|
||||||
import 'package:ente_auth/ui/code_widget.dart';
|
import 'package:ente_auth/ui/code_widget.dart';
|
||||||
import 'package:ente_auth/ui/common/loading_widget.dart';
|
import 'package:ente_auth/ui/common/loading_widget.dart';
|
||||||
|
import 'package:ente_auth/ui/home/coach_mark_widget.dart';
|
||||||
|
import 'package:ente_auth/ui/home/home_empty_state.dart';
|
||||||
import 'package:ente_auth/ui/home/speed_dial_label_widget.dart';
|
import 'package:ente_auth/ui/home/speed_dial_label_widget.dart';
|
||||||
import 'package:ente_auth/ui/scanner_page.dart';
|
import 'package:ente_auth/ui/scanner_page.dart';
|
||||||
import 'package:ente_auth/ui/settings_page.dart';
|
import 'package:ente_auth/ui/settings_page.dart';
|
||||||
|
@ -206,7 +207,10 @@ class _HomePageState extends State<HomePage> {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
if (_hasLoaded) {
|
if (_hasLoaded) {
|
||||||
if (_filteredCodes.isEmpty && _searchText.isEmpty) {
|
if (_filteredCodes.isEmpty && _searchText.isEmpty) {
|
||||||
return _getEmptyState();
|
return HomeEmptyStateWidget(
|
||||||
|
onScanTap: _redirectToManualEntryPage,
|
||||||
|
onManuallySetupTap: _redirectToManualEntryPage,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
final list = ListView.builder(
|
final list = ListView.builder(
|
||||||
itemBuilder: ((context, index) {
|
itemBuilder: ((context, index) {
|
||||||
|
@ -218,7 +222,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
list,
|
list,
|
||||||
_getCoachMarkWidget(),
|
const CoachMarkWidget(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else if (_showSearchBox) {
|
} else if (_showSearchBox) {
|
||||||
|
@ -291,110 +295,4 @@ class _HomePageState extends State<HomePage> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _getEmptyState() {
|
|
||||||
final l10n = context.l10n;
|
|
||||||
return Center(
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints.tightFor(height: 800, width: 450),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
"assets/wallet-front-gradient.png",
|
|
||||||
width: 200,
|
|
||||||
height: 200,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
l10n.setupFirstAccount,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context).textTheme.headline4,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 64),
|
|
||||||
SizedBox(
|
|
||||||
width: 400,
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: _redirectToScannerPage,
|
|
||||||
child: Text(l10n.importScanQrCode),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 18),
|
|
||||||
SizedBox(
|
|
||||||
width: 400,
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: _redirectToManualEntryPage,
|
|
||||||
child: Text(l10n.importEnterSetupKey),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _getCoachMarkWidget() {
|
|
||||||
final l10n = context.l10n;
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
await PreferenceService.instance.setHasShownCoachMark(true);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
color: Theme.of(context).colorScheme.background.withOpacity(0.1),
|
|
||||||
child: BackdropFilter(
|
|
||||||
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.swipe_left,
|
|
||||||
size: 42,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
l10n.swipeHint,
|
|
||||||
style: Theme.of(context).textTheme.headline6,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 36,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 160,
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await PreferenceService.instance
|
|
||||||
.setHasShownCoachMark(true);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
child: Text(l10n.ok),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue