fix onboarding to single page

This commit is contained in:
Dr-Blank 2024-05-10 04:11:39 -04:00
parent 6c60d1c6ed
commit d9345cad2b
No known key found for this signature in database
GPG key ID: 7452CC63F210A266
14 changed files with 422 additions and 325 deletions

View file

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

View file

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

View 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');
}
}

View file

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

View file

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

View file

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

View file

@ -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'), ),
), ],
], ),
), ),
), ),
), ),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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