mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-06 02:59:28 +00:00
fix onboarding to single page
This commit is contained in:
parent
6c60d1c6ed
commit
d9345cad2b
14 changed files with 422 additions and 325 deletions
|
|
@ -40,15 +40,20 @@ class MyApp extends ConsumerWidget {
|
||||||
return apiSettings.activeUser == null || servers.isEmpty;
|
return apiSettings.activeUser == null || servers.isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var routerConfig = MyAppRouter(needOnboarding: needOnboarding()).config;
|
||||||
|
// if (needOnboarding()) {
|
||||||
|
// routerConfig.goNamed(Routes.onboarding);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
theme: lightTheme,
|
theme: lightTheme,
|
||||||
darkTheme: darkTheme,
|
darkTheme: darkTheme,
|
||||||
themeMode: ref.watch(appSettingsProvider).isDarkMode
|
themeMode: ref.watch(appSettingsProvider).isDarkMode
|
||||||
? ThemeMode.dark
|
? ThemeMode.dark
|
||||||
: ThemeMode.light,
|
: ThemeMode.light,
|
||||||
routerConfig: MyAppRouter(needOnboarding: needOnboarding()).config,
|
routerConfig: routerConfig,
|
||||||
// routerConfig: _router.config,
|
// routerConfig: _router.config,
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
lib/pages/navigation.dart
Normal file
32
lib/pages/navigation.dart
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
class AppNavigation extends HookConsumerWidget {
|
||||||
|
const AppNavigation({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final currentIndex = useState(0);
|
||||||
|
return Scaffold(
|
||||||
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
currentIndex: currentIndex.value,
|
||||||
|
onTap: (value) => currentIndex.value = value,
|
||||||
|
items: const [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.home),
|
||||||
|
label: 'Home',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.business),
|
||||||
|
label: 'Business',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.school),
|
||||||
|
label: 'School',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
import 'package:coast/coast.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:whispering_pages/pages/onboarding/server_setup.dart';
|
|
||||||
import 'package:whispering_pages/pages/onboarding/user_login.dart';
|
|
||||||
import 'package:whispering_pages/settings/api_settings_provider.dart';
|
|
||||||
|
|
||||||
const _serverTag = 'server';
|
|
||||||
|
|
||||||
class OnboardingPage extends StatefulHookConsumerWidget {
|
|
||||||
const OnboardingPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
OnboardingPageState createState() => OnboardingPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class OnboardingPageState extends ConsumerState<OnboardingPage> {
|
|
||||||
final coastController = CoastController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
coastController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final apiSettings = ref.watch(apiSettingsProvider);
|
|
||||||
|
|
||||||
final serverUriController = useTextEditingController(
|
|
||||||
text: apiSettings.activeServer?.serverUrl.toString(),
|
|
||||||
);
|
|
||||||
|
|
||||||
bool isUserLoginAvailable() {
|
|
||||||
return apiSettings.activeServer != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore: invalid_use_of_protected_member
|
|
||||||
if (isUserLoginAvailable()) {
|
|
||||||
try {
|
|
||||||
coastController.animateTo(
|
|
||||||
beach: 1,
|
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('Error: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final beaches = [
|
|
||||||
Beach(
|
|
||||||
builder: (context) => FirstTimeServerSetupPage(
|
|
||||||
controller: serverUriController,
|
|
||||||
heroServerTag: _serverTag,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
isUserLoginAvailable()
|
|
||||||
? Beach(
|
|
||||||
builder: (context) => FirstTimeUserLoginPage(
|
|
||||||
serverUriController: serverUriController,
|
|
||||||
heroServerTag: _serverTag,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
].nonNulls.toList();
|
|
||||||
const activeStep = 0;
|
|
||||||
return Scaffold(
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Coast(
|
|
||||||
beaches: beaches,
|
|
||||||
controller: coastController,
|
|
||||||
observers: [
|
|
||||||
CrabController(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
child: Container(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
215
lib/pages/onboarding/onboarding_single_page.dart
Normal file
215
lib/pages/onboarding/onboarding_single_page.dart
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:whispering_pages/api/api_provider.dart';
|
||||||
|
import 'package:whispering_pages/api/authenticated_user_provider.dart';
|
||||||
|
import 'package:whispering_pages/api/server_provider.dart';
|
||||||
|
import 'package:whispering_pages/settings/api_settings_provider.dart';
|
||||||
|
import 'package:whispering_pages/settings/models/models.dart' as model;
|
||||||
|
import 'package:whispering_pages/widgets/add_new_server.dart';
|
||||||
|
import 'package:whispering_pages/widgets/user_login.dart';
|
||||||
|
|
||||||
|
class OnboardingSinglePage extends HookConsumerWidget {
|
||||||
|
const OnboardingSinglePage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final apiSettings = ref.watch(apiSettingsProvider);
|
||||||
|
final serverUriController = useTextEditingController(
|
||||||
|
text: apiSettings.activeServer?.serverUrl.toString() ?? '',
|
||||||
|
);
|
||||||
|
final api = ref
|
||||||
|
.watch(audiobookshelfApiProvider(Uri.https(serverUriController.text)));
|
||||||
|
|
||||||
|
final isUserLoginAvailable = useState(apiSettings.activeServer != null);
|
||||||
|
|
||||||
|
final usernameController = useTextEditingController();
|
||||||
|
final passwordController = useTextEditingController();
|
||||||
|
|
||||||
|
// reverse the animation if the user is not logged in
|
||||||
|
void addServer() {
|
||||||
|
var newServer = serverUriController.text.isEmpty
|
||||||
|
? null
|
||||||
|
: model.AudiobookShelfServer(
|
||||||
|
serverUrl: Uri.parse(serverUriController.text),
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
// add the server to the list of servers
|
||||||
|
if (newServer != null) {
|
||||||
|
ref.read(audiobookShelfServerProvider.notifier).addServer(
|
||||||
|
newServer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// else remove the server from the list of servers
|
||||||
|
else if (apiSettings.activeServer != null) {
|
||||||
|
ref
|
||||||
|
.read(audiobookShelfServerProvider.notifier)
|
||||||
|
.removeServer(apiSettings.activeServer!);
|
||||||
|
}
|
||||||
|
} on ServerAlreadyExistsException catch (e) {
|
||||||
|
newServer = e.server;
|
||||||
|
} finally {
|
||||||
|
ref.read(apiSettingsProvider.notifier).updateState(
|
||||||
|
apiSettings.copyWith(
|
||||||
|
activeServer: newServer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Login to the server and save the user
|
||||||
|
Future<void> loginAndSave() async {
|
||||||
|
final username = usernameController.text;
|
||||||
|
final password = passwordController.text;
|
||||||
|
final success = await api.login(username: username, password: password);
|
||||||
|
if (success != null) {
|
||||||
|
// save the server
|
||||||
|
addServer();
|
||||||
|
var authenticatedUser = model.AuthenticatedUser(
|
||||||
|
server: model.AudiobookShelfServer(
|
||||||
|
serverUrl: Uri.parse(serverUriController.text),
|
||||||
|
),
|
||||||
|
id: success.user.id,
|
||||||
|
password: password,
|
||||||
|
username: username,
|
||||||
|
authToken: api.token!,
|
||||||
|
);
|
||||||
|
// add the user to the list of users
|
||||||
|
ref.read(authenticatedUserProvider.notifier).addUser(authenticatedUser);
|
||||||
|
|
||||||
|
// set the active user
|
||||||
|
ref.read(apiSettingsProvider.notifier).updateState(
|
||||||
|
apiSettings.copyWith(
|
||||||
|
activeUser: authenticatedUser,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Login failed'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// give focus back to the username field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'Welcome to Whispering Pages',
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox.square(
|
||||||
|
dimension: 16.0,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'Please enter the URL of your AudiobookShelf Server',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: AddNewServer(
|
||||||
|
controller: serverUriController,
|
||||||
|
allowEmpty: true,
|
||||||
|
onPressed: () {
|
||||||
|
isUserLoginAvailable.value =
|
||||||
|
serverUriController.text.isNotEmpty;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: 500.ms,
|
||||||
|
transitionBuilder: (child, animation) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: SlideTransition(
|
||||||
|
position: Tween<Offset>(
|
||||||
|
begin: const Offset(0, 0.3),
|
||||||
|
end: const Offset(0, 0),
|
||||||
|
).animate(animation),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: isUserLoginAvailable.value
|
||||||
|
? UserLogin(
|
||||||
|
passwordController: passwordController,
|
||||||
|
usernameController: usernameController,
|
||||||
|
onPressed: loginAndSave,
|
||||||
|
)
|
||||||
|
// ).animate().fade(duration: 600.ms).slideY(begin: 0.3, end: 0)
|
||||||
|
: const RedirectToABS().animate().fadeIn().slideY(
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
duration: 500.ms,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RedirectToABS extends StatelessWidget {
|
||||||
|
const RedirectToABS({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FittedBox(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// a simple text with hyper link to only the "click here" part
|
||||||
|
const Text('Do not have a server? '),
|
||||||
|
// a simple text with hyper link to the github page
|
||||||
|
TextButton(
|
||||||
|
autofocus: false,
|
||||||
|
isSemanticButton: false,
|
||||||
|
style: ButtonStyle(
|
||||||
|
elevation: MaterialStateProperty.all(0),
|
||||||
|
padding: MaterialStateProperty.all(
|
||||||
|
const EdgeInsets.all(0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
// open the github page
|
||||||
|
// ignore: avoid_print
|
||||||
|
print('Opening the github page');
|
||||||
|
await _launchUrl(
|
||||||
|
Uri.parse(
|
||||||
|
'https://www.audiobookshelf.org',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Click here'),
|
||||||
|
),
|
||||||
|
const Text(' to know how to setup a server.'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _launchUrl(Uri url) async {
|
||||||
|
if (!await launchUrl(
|
||||||
|
url,
|
||||||
|
mode: LaunchMode.inAppWebView,
|
||||||
|
webOnlyWindowName: '_blank',
|
||||||
|
)) {
|
||||||
|
// throw Exception('Could not launch $url');
|
||||||
|
debugPrint('Could not launch $url');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import 'package:coast/coast.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:whispering_pages/api/server_provider.dart';
|
|
||||||
import 'package:whispering_pages/settings/models/audiobookshelf_server.dart' as model;
|
|
||||||
import 'package:whispering_pages/settings/api_settings_provider.dart';
|
|
||||||
import 'package:whispering_pages/widgets/add_new_server.dart';
|
|
||||||
|
|
||||||
class FirstTimeServerSetupPage extends HookConsumerWidget {
|
|
||||||
const FirstTimeServerSetupPage({
|
|
||||||
super.key,
|
|
||||||
required this.controller,
|
|
||||||
required this.heroServerTag,
|
|
||||||
});
|
|
||||||
final TextEditingController controller;
|
|
||||||
final String heroServerTag;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final apiSettings = ref.watch(apiSettingsProvider);
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Text('Welcome to Whispering Pages'),
|
|
||||||
Crab(
|
|
||||||
tag: heroServerTag,
|
|
||||||
child: AddNewServer(
|
|
||||||
controller: controller,
|
|
||||||
allowEmpty: true,
|
|
||||||
onPressed: () {
|
|
||||||
var newServer = controller.text.isEmpty
|
|
||||||
? null
|
|
||||||
: model.AudiobookShelfServer(
|
|
||||||
serverUrl: Uri.parse(controller.text),
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
// add the server to the list of servers
|
|
||||||
if (newServer != null) {
|
|
||||||
ref.read(audiobookShelfServerProvider.notifier).addServer(
|
|
||||||
newServer,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// else remove the server from the list of servers
|
|
||||||
else if (apiSettings.activeServer != null) {
|
|
||||||
ref
|
|
||||||
.read(audiobookShelfServerProvider.notifier)
|
|
||||||
.removeServer(apiSettings.activeServer!);
|
|
||||||
}
|
|
||||||
} on ServerAlreadyExistsException catch (e) {
|
|
||||||
newServer = e.server;
|
|
||||||
} finally {
|
|
||||||
ref.read(apiSettingsProvider.notifier).updateState(
|
|
||||||
apiSettings.copyWith(
|
|
||||||
activeServer: newServer,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
import 'package:coast/coast.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:whispering_pages/api/api_provider.dart';
|
|
||||||
import 'package:whispering_pages/api/authenticated_user_provider.dart'
|
|
||||||
show authenticatedUserProvider;
|
|
||||||
import 'package:whispering_pages/settings/models/audiobookshelf_server.dart';
|
|
||||||
import 'package:whispering_pages/settings/models/authenticated_user.dart';
|
|
||||||
import 'package:whispering_pages/settings/api_settings_provider.dart';
|
|
||||||
import 'package:whispering_pages/widgets/add_new_server.dart';
|
|
||||||
import 'package:whispering_pages/widgets/user_login.dart';
|
|
||||||
|
|
||||||
|
|
||||||
/// Once the user has selected a server, they can login to it.
|
|
||||||
class FirstTimeUserLoginPage extends HookConsumerWidget {
|
|
||||||
const FirstTimeUserLoginPage({
|
|
||||||
super.key,
|
|
||||||
required this.serverUriController,
|
|
||||||
required this.heroServerTag,
|
|
||||||
});
|
|
||||||
|
|
||||||
final TextEditingController serverUriController;
|
|
||||||
final String heroServerTag;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final usernameController = useTextEditingController();
|
|
||||||
final passwordController = useTextEditingController();
|
|
||||||
|
|
||||||
final apiSettings = ref.watch(apiSettingsProvider);
|
|
||||||
final api = ref
|
|
||||||
.watch(audiobookshelfApiProvider(Uri.https(serverUriController.text)));
|
|
||||||
|
|
||||||
/// Login to the server and save the user
|
|
||||||
void loginAndSave() async {
|
|
||||||
final username = usernameController.text;
|
|
||||||
final password = passwordController.text;
|
|
||||||
final success = await api.login(username: username, password: password);
|
|
||||||
// debugPrint('Login success: $success');
|
|
||||||
if (success != null) {
|
|
||||||
var authenticatedUser = AuthenticatedUser(
|
|
||||||
server: AudiobookShelfServer(
|
|
||||||
serverUrl: Uri.parse(serverUriController.text),
|
|
||||||
),
|
|
||||||
id: success.user.id,
|
|
||||||
password: password,
|
|
||||||
username: username,
|
|
||||||
authToken: api.token!,
|
|
||||||
);
|
|
||||||
// add the user to the list of users
|
|
||||||
ref.read(authenticatedUserProvider.notifier).addUser(authenticatedUser);
|
|
||||||
|
|
||||||
// set the active user
|
|
||||||
ref.read(apiSettingsProvider.notifier).updateState(
|
|
||||||
apiSettings.copyWith(
|
|
||||||
activeUser: authenticatedUser,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(
|
|
||||||
content: Text('Login failed'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// give focus back to the username field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Crab(
|
|
||||||
tag: heroServerTag,
|
|
||||||
child: AddNewServer(
|
|
||||||
controller: serverUriController,
|
|
||||||
onPressed: () {},
|
|
||||||
readOnly: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
const Text('Login to server'),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
UserLogin(
|
|
||||||
usernameController: usernameController,
|
|
||||||
passwordController: passwordController,
|
|
||||||
onPressed: loginAndSave,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'package:go_router/go_router.dart';
|
||||||
import 'package:whispering_pages/pages/home_page.dart';
|
import 'package:whispering_pages/pages/home_page.dart';
|
||||||
import 'package:whispering_pages/pages/library_item_page.dart';
|
import 'package:whispering_pages/pages/library_item_page.dart';
|
||||||
import 'package:whispering_pages/pages/library_page.dart';
|
import 'package:whispering_pages/pages/library_page.dart';
|
||||||
import 'package:whispering_pages/pages/onboarding/onboarding.dart';
|
import 'package:whispering_pages/pages/onboarding/onboarding_single_page.dart';
|
||||||
|
|
||||||
part 'constants.dart';
|
part 'constants.dart';
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ class MyAppRouter {
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: Routes.onboarding,
|
name: Routes.onboarding,
|
||||||
builder: (context, state) => const OnboardingPage(),
|
builder: (context, state) => const OnboardingSinglePage(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/',
|
path: '/',
|
||||||
|
|
@ -44,7 +44,7 @@ class MyAppRouter {
|
||||||
],
|
],
|
||||||
redirect: (context, state) {
|
redirect: (context, state) {
|
||||||
if (needOnboarding) {
|
if (needOnboarding) {
|
||||||
return context.namedLocation(Routes.onboarding);
|
return config.namedLocation(Routes.onboarding);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -42,75 +42,78 @@ class UserLogin extends HookConsumerWidget {
|
||||||
return Center(
|
return Center(
|
||||||
child: InactiveFocusScopeObserver(
|
child: InactiveFocusScopeObserver(
|
||||||
child: AutofillGroup(
|
child: AutofillGroup(
|
||||||
child: Column(
|
child: Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
padding: const EdgeInsets.all(8.0),
|
||||||
children: [
|
child: Column(
|
||||||
TextFormField(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
controller: usernameController,
|
children: [
|
||||||
autofocus: true,
|
TextFormField(
|
||||||
autofillHints: const [AutofillHints.username],
|
controller: usernameController,
|
||||||
textInputAction: TextInputAction.next,
|
autofocus: true,
|
||||||
decoration: InputDecoration(
|
autofillHints: const [AutofillHints.username],
|
||||||
labelText: 'Username',
|
textInputAction: TextInputAction.next,
|
||||||
labelStyle: TextStyle(
|
decoration: InputDecoration(
|
||||||
color: Theme.of(context)
|
labelText: 'Username',
|
||||||
.colorScheme
|
labelStyle: TextStyle(
|
||||||
.onBackground
|
color: Theme.of(context)
|
||||||
.withOpacity(0.8),
|
.colorScheme
|
||||||
),
|
.onBackground
|
||||||
border: const OutlineInputBorder(),
|
.withOpacity(0.8),
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TextFormField(
|
|
||||||
controller: passwordController,
|
|
||||||
autofillHints: const [AutofillHints.password],
|
|
||||||
textInputAction: TextInputAction.done,
|
|
||||||
obscureText: !isPasswordVisible.value,
|
|
||||||
onFieldSubmitted: onPressed != null
|
|
||||||
? (_) {
|
|
||||||
onPressed!();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Password',
|
|
||||||
labelStyle: TextStyle(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onBackground
|
|
||||||
.withOpacity(0.8),
|
|
||||||
),
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
suffixIcon: ColorFiltered(
|
|
||||||
colorFilter: ColorFilter.mode(
|
|
||||||
Theme.of(context).colorScheme.primary.withOpacity(0.8),
|
|
||||||
BlendMode.srcIn,
|
|
||||||
),
|
),
|
||||||
child: InkWell(
|
border: const OutlineInputBorder(),
|
||||||
borderRadius: BorderRadius.circular(50),
|
),
|
||||||
onTap: () {
|
),
|
||||||
isPasswordVisible.value = !isPasswordVisible.value;
|
const SizedBox(height: 10),
|
||||||
},
|
TextFormField(
|
||||||
child: Container(
|
controller: passwordController,
|
||||||
margin: const EdgeInsets.only(left: 8, right: 8),
|
autofillHints: const [AutofillHints.password],
|
||||||
child: Lottie.asset(
|
textInputAction: TextInputAction.done,
|
||||||
'assets/animations/Animation - 1714930099660.json',
|
obscureText: !isPasswordVisible.value,
|
||||||
controller: isPasswordVisibleAnimationController,
|
onFieldSubmitted: onPressed != null
|
||||||
|
? (_) {
|
||||||
|
onPressed!();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Password',
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onBackground
|
||||||
|
.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
suffixIcon: ColorFiltered(
|
||||||
|
colorFilter: ColorFilter.mode(
|
||||||
|
Theme.of(context).colorScheme.primary.withOpacity(0.8),
|
||||||
|
BlendMode.srcIn,
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(50),
|
||||||
|
onTap: () {
|
||||||
|
isPasswordVisible.value = !isPasswordVisible.value;
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(left: 8, right: 8),
|
||||||
|
child: Lottie.asset(
|
||||||
|
'assets/animations/Animation - 1714930099660.json',
|
||||||
|
controller: isPasswordVisibleAnimationController,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
suffixIconConstraints: const BoxConstraints(
|
||||||
suffixIconConstraints: const BoxConstraints(
|
maxHeight: 45,
|
||||||
maxHeight: 45,
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 30),
|
||||||
const SizedBox(height: 30),
|
ElevatedButton(
|
||||||
ElevatedButton(
|
onPressed: onPressed,
|
||||||
onPressed: onPressed,
|
child: const Text('Login'),
|
||||||
child: const Text('Login'),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,13 @@
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
|
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar =
|
g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin");
|
||||||
isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar);
|
isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
isar_flutter_libs
|
isar_flutter_libs
|
||||||
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|
|
||||||
94
pubspec.lock
94
pubspec.lock
|
|
@ -334,6 +334,14 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_animate:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_animate
|
||||||
|
sha256: "7c8a6594a9252dad30cc2ef16e33270b6248c4dedc3b3d06c86c4f3f4dc05ae5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.5.0"
|
||||||
flutter_cache_manager:
|
flutter_cache_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -354,10 +362,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_lints
|
name: flutter_lints
|
||||||
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
|
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "4.0.0"
|
||||||
flutter_riverpod:
|
flutter_riverpod:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -374,6 +382,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
flutter_shaders:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_shaders
|
||||||
|
sha256: "02750b545c01ff4d8e9bbe8f27a7731aa3778402506c67daa1de7f5fc3f4befe"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -420,10 +436,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: "9e0f7d1a3e7dc5010903e330fbc5497872c4c3cf6626381d69083cc1d5113c1e"
|
sha256: "81f94e16d7063b60f0da3a79f872e140d6518f306749303bf981abd7d6b46734"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.0.2"
|
version: "14.1.0"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -556,10 +572,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: lints
|
name: lints
|
||||||
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
|
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "4.0.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -956,6 +972,70 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
|
url_launcher:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.6"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.1"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.5"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1030,4 +1110,4 @@ packages:
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.3.4 <4.0.0"
|
dart: ">=3.3.4 <4.0.0"
|
||||||
flutter: ">=3.16.0"
|
flutter: ">=3.19.0"
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ dependencies:
|
||||||
easy_stepper: ^0.8.4
|
easy_stepper: ^0.8.4
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_animate: ^4.5.0
|
||||||
flutter_cache_manager: ^3.3.2
|
flutter_cache_manager: ^3.3.2
|
||||||
flutter_hooks: ^0.20.5
|
flutter_hooks: ^0.20.5
|
||||||
flutter_settings_ui: ^3.0.1
|
flutter_settings_ui: ^3.0.1
|
||||||
|
|
@ -58,10 +59,11 @@ dependencies:
|
||||||
shelfsdk:
|
shelfsdk:
|
||||||
path: ../../_dart/shelfsdk
|
path: ../../_dart/shelfsdk
|
||||||
shimmer: ^3.0.0
|
shimmer: ^3.0.0
|
||||||
|
url_launcher: ^6.2.6
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.9
|
build_runner: ^2.4.9
|
||||||
custom_lint: ^0.6.4
|
custom_lint: ^0.6.4
|
||||||
flutter_lints: ^3.0.0
|
flutter_lints: ^4.0.0
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
freezed: ^2.5.2
|
freezed: ^2.5.2
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,11 @@
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
|
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
IsarFlutterLibsPluginRegisterWithRegistrar(
|
IsarFlutterLibsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin"));
|
registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
isar_flutter_libs
|
isar_flutter_libs
|
||||||
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue