From c693b0dcd7eb3b0074d778db7e442b2d5859210a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 14 Apr 2024 15:58:27 +0800 Subject: [PATCH] :sparkles: New moment --- ios/Podfile.lock | 6 + ios/Runner/Info.plist | 6 + lib/i18n/app_en.arb | 11 +- lib/i18n/app_zh.arb | 9 +- lib/providers/auth.dart | 9 +- lib/screens/account.dart | 18 +- lib/screens/explore.dart | 14 +- lib/screens/posts/new_moment.dart | 144 ++++++++- lib/screens/posts/screen.dart | 5 +- .../{wrapper.dart => common_wrapper.dart} | 4 +- lib/widgets/indent_wrapper.dart | 25 ++ lib/widgets/posts/attachment_editor.dart | 300 ++++++++++++++++++ lib/widgets/posts/item.dart | 2 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 122 ++++++- pubspec.yaml | 2 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 20 files changed, 655 insertions(+), 33 deletions(-) rename lib/widgets/{wrapper.dart => common_wrapper.dart} (86%) create mode 100644 lib/widgets/indent_wrapper.dart create mode 100755 lib/widgets/posts/attachment_editor.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b30b33d..86f0f95 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2,6 +2,8 @@ PODS: - Flutter (1.0.0) - flutter_secure_storage (6.0.0): - Flutter + - image_picker_ios (0.0.1): + - Flutter - media_kit_video (0.0.1): - Flutter - package_info_plus (0.4.5): @@ -26,6 +28,7 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) @@ -41,6 +44,8 @@ EXTERNAL SOURCES: :path: Flutter flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" media_kit_video: :path: ".symlinks/plugins/media_kit_video/ios" package_info_plus: @@ -63,6 +68,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be + image_picker_ios: b545a5f16c0fa88e3ecbbce3ed4de45567a8ec18 media_kit_video: 26c5b265a4094a2df3e8d41e6724d9b964c13151 package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 4af3b37..bb54496 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -35,6 +35,12 @@ UILaunchStoryboardName LaunchScreen + NSPhotoLibraryUsageDescription + Allow you add photo to your message or post + NSCameraUsageDescription + Allow you take photo/video for your message or post + NSMicrophoneUsageDescription + Allow you record audio for your message or post UIMainStoryboardFile Main UISupportedInterfaceOrientations diff --git a/lib/i18n/app_en.arb b/lib/i18n/app_en.arb index eff1fd3..7fb89e9 100644 --- a/lib/i18n/app_en.arb +++ b/lib/i18n/app_en.arb @@ -7,5 +7,12 @@ "signUp": "Sign Up", "signUpCaption": "Create an account on Solarpass and then get the access of entire Solar Networks!", "post": "Post", - "comment": "Comment" -} \ No newline at end of file + "postVerb": "Post", + "comment": "Comment", + "attachment": "Attachment", + "attachmentAdd": "Add new attachment", + "pickPhoto": "Gallery photo", + "newMoment": "Record a moment", + "postIdentityNotify": "You will create this post as", + "postContentPlaceholder": "What's happened?!" +} diff --git a/lib/i18n/app_zh.arb b/lib/i18n/app_zh.arb index 7fe6c15..348e1f4 100644 --- a/lib/i18n/app_zh.arb +++ b/lib/i18n/app_zh.arb @@ -7,5 +7,12 @@ "signUp": "注册", "signUpCaption": "在 Solarpass 注册一个账号以获得整个 Solar Networks 的存取权!", "post": "帖子", - "comment": "评论" + "postVerb": "发表", + "comment": "评论", + "attachment": "附件", + "attachmentAdd": "附加新附件", + "pickPhoto": "相册照片", + "newMoment": "记录时刻", + "postIdentityNotify": "你将会以该身份发表本帖子", + "postContentPlaceholder": "发生什么事了?!" } \ No newline at end of file diff --git a/lib/providers/auth.dart b/lib/providers/auth.dart index 753ed9a..39e86a2 100755 --- a/lib/providers/auth.dart +++ b/lib/providers/auth.dart @@ -7,10 +7,10 @@ import 'package:solian/screens/auth.dart'; import 'package:oauth2/oauth2.dart' as oauth2; import 'package:solian/utils/service_url.dart'; -final authClient = AuthProvider(); - class AuthProvider { - AuthProvider(); + AuthProvider() { + pickClient(); + } final deviceEndpoint = getRequestUri('passport', '/api/notifications/subscribe'); @@ -61,7 +61,8 @@ class AuthProvider { basicAuth: false, ); - var authorizationUrl = grant.getAuthorizationUrl(redirectUrl, scopes: ["openid"]); + var authorizationUrl = + grant.getAuthorizationUrl(redirectUrl, scopes: ["openid"]); if (Platform.isAndroid || Platform.isIOS) { // Use WebView to get authorization url diff --git a/lib/screens/account.dart b/lib/screens/account.dart index fa51be5..e717123 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -3,7 +3,7 @@ import 'package:provider/provider.dart'; import 'package:solian/providers/auth.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:solian/utils/service_url.dart'; -import 'package:solian/widgets/wrapper.dart'; +import 'package:solian/widgets/common_wrapper.dart'; import 'package:url_launcher/url_launcher.dart'; class AccountScreen extends StatefulWidget { @@ -66,7 +66,7 @@ class _AccountScreenState extends State { caption: AppLocalizations.of(context)!.signInCaption, onTap: () { auth.signIn(context).then((_) { - authClient.isAuthorized().then((val) { + auth.isAuthorized().then((val) { setState(() => isAuthorized = val); }); }); @@ -90,13 +90,15 @@ class _AccountScreenState extends State { class NameCard extends StatelessWidget { const NameCard({super.key}); - Future renderAvatar() async { - final profiles = await authClient.getProfiles(); + Future renderAvatar(BuildContext context) async { + final auth = context.read(); + final profiles = await auth.getProfiles(); return CircleAvatar(backgroundImage: NetworkImage(profiles["picture"])); } - Future renderLabel() async { - final profiles = await authClient.getProfiles(); + Future renderLabel(BuildContext context) async { + final auth = context.read(); + final profiles = await auth.getProfiles(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -122,7 +124,7 @@ class NameCard extends StatelessWidget { child: Row( children: [ FutureBuilder( - future: renderAvatar(), + future: renderAvatar(context), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { @@ -134,7 +136,7 @@ class NameCard extends StatelessWidget { ), const SizedBox(width: 20), FutureBuilder( - future: renderLabel(), + future: renderLabel(context), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart index 16e0d8d..d4a7baf 100644 --- a/lib/screens/explore.dart +++ b/lib/screens/explore.dart @@ -8,8 +8,8 @@ import 'package:solian/utils/service_url.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:http/http.dart' as http; +import 'package:solian/widgets/indent_wrapper.dart'; import 'package:solian/widgets/posts/item.dart'; -import 'package:solian/widgets/wrapper.dart'; class ExploreScreen extends StatefulWidget { const ExploreScreen({super.key}); @@ -58,7 +58,15 @@ class _ExploreScreenState extends State { @override Widget build(BuildContext context) { - return LayoutWrapper( + return IndentWrapper( + noSafeArea: true, + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.edit), + onPressed: () async { + final did = await router.pushNamed("posts.moments.new"); + if (did == true) _pagingController.refresh(); + }, + ), title: AppLocalizations.of(context)!.explore, child: RefreshIndicator( onRefresh: () => Future.sync( @@ -75,7 +83,7 @@ class _ExploreScreenState extends State { itemBuilder: (context, item, index) => GestureDetector( child: PostItem(item: item), onTap: () { - router.goNamed( + router.pushNamed( 'posts.screen', pathParameters: { 'alias': item.alias, diff --git a/lib/screens/posts/new_moment.dart b/lib/screens/posts/new_moment.dart index 9475789..87b6fe6 100644 --- a/lib/screens/posts/new_moment.dart +++ b/lib/screens/posts/new_moment.dart @@ -1,19 +1,143 @@ -import 'package:flutter/material.dart'; +import 'dart:convert'; -class NewMomentScreen extends StatelessWidget { +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:solian/models/post.dart'; +import 'package:solian/providers/auth.dart'; +import 'package:solian/router.dart'; +import 'package:solian/utils/service_url.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:solian/widgets/indent_wrapper.dart'; +import 'package:solian/widgets/posts/attachment_editor.dart'; + +class NewMomentScreen extends StatefulWidget { const NewMomentScreen({super.key}); @override - Widget build(BuildContext context) { - return Center( - child: Container( - constraints: const BoxConstraints(maxWidth: 640), - child: Column( - children: [ + State createState() => _NewMomentScreenState(); +} - ], +class _NewMomentScreenState extends State { + final _textController = TextEditingController(); + + bool _isSubmitting = false; + + List _attachments = List.empty(growable: true); + + void viewAttachments(BuildContext context) { + showModalBottomSheet( + context: context, + builder: (context) => AttachmentEditor( + current: _attachments, + onUpdate: (value) => _attachments = value, + ), + ); + } + + Future createPost(BuildContext context) async { + final auth = context.read(); + if (!await auth.isAuthorized()) return; + + setState(() => _isSubmitting = true); + var res = await auth.client!.post( + getRequestUri('interactive', '/api/p/moments'), + headers: { + 'Content-Type': 'application/json', + }, + body: jsonEncode({ + 'content': _textController.value.text, + 'attachments': _attachments, + }), + ); + if (res.statusCode != 200) { + var message = utf8.decode(res.bodyBytes); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Something went wrong... $message")), + ); + } else { + if (router.canPop()) { + router.pop(true); + } + } + setState(() => _isSubmitting = false); + } + + @override + Widget build(BuildContext context) { + final auth = context.read(); + + return IndentWrapper( + hideDrawer: true, + title: AppLocalizations.of(context)!.newMoment, + appBarActions: [ + TextButton( + onPressed: !_isSubmitting ? () => createPost(context) : null, + child: Text(AppLocalizations.of(context)!.postVerb), + ), + ], + child: Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 640), + child: Column( + children: [ + _isSubmitting ? const LinearProgressIndicator() : Container(), + FutureBuilder( + future: auth.getProfiles(), + builder: (context, snapshot) { + if (snapshot.hasData) { + var userinfo = snapshot.data; + return ListTile( + title: Text(userinfo["nick"]), + subtitle: Text( + AppLocalizations.of(context)!.postIdentityNotify, + ), + leading: CircleAvatar( + backgroundImage: NetworkImage(userinfo["picture"]), + ), + ); + } else { + return Container(); + } + }, + ), + const Divider(thickness: 0.3), + Expanded( + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: TextField( + maxLines: null, + autofocus: true, + autocorrect: true, + keyboardType: TextInputType.multiline, + controller: _textController, + decoration: InputDecoration.collapsed( + hintText: + AppLocalizations.of(context)!.postContentPlaceholder, + ), + ), + ), + ), + Container( + decoration: const BoxDecoration( + border: Border( + top: BorderSide(width: 0.3, color: Color(0xffdedede)), + ), + ), + child: Row( + children: [ + TextButton( + style: TextButton.styleFrom(shape: const CircleBorder()), + child: const Icon(Icons.camera_alt), + onPressed: () => viewAttachments(context), + ) + ], + ), + ), + ], + ), ), ), ); } -} \ No newline at end of file +} diff --git a/lib/screens/posts/screen.dart b/lib/screens/posts/screen.dart index e874911..543dbba 100644 --- a/lib/screens/posts/screen.dart +++ b/lib/screens/posts/screen.dart @@ -5,9 +5,9 @@ import 'package:http/http.dart' as http; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:solian/models/post.dart'; import 'package:solian/utils/service_url.dart'; +import 'package:solian/widgets/indent_wrapper.dart'; import 'package:solian/widgets/posts/comment_list.dart'; import 'package:solian/widgets/posts/item.dart'; -import 'package:solian/widgets/wrapper.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class PostScreen extends StatefulWidget { @@ -43,7 +43,8 @@ class _PostScreenState extends State { @override Widget build(BuildContext context) { - return LayoutWrapper( + return IndentWrapper( + hideDrawer: true, title: AppLocalizations.of(context)!.post, child: FutureBuilder( future: fetchPost(context), diff --git a/lib/widgets/wrapper.dart b/lib/widgets/common_wrapper.dart similarity index 86% rename from lib/widgets/wrapper.dart rename to lib/widgets/common_wrapper.dart index 3befc6e..7dd12dc 100644 --- a/lib/widgets/wrapper.dart +++ b/lib/widgets/common_wrapper.dart @@ -12,7 +12,9 @@ class LayoutWrapper extends StatelessWidget { return Scaffold( drawer: const SolianNavigationDrawer(), appBar: AppBar(title: Text(title)), - body: child ?? Container(), + body: SafeArea( + child: child ?? Container(), + ), ); } } diff --git a/lib/widgets/indent_wrapper.dart b/lib/widgets/indent_wrapper.dart new file mode 100644 index 0000000..4029f2e --- /dev/null +++ b/lib/widgets/indent_wrapper.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:solian/widgets/navigation_drawer.dart'; + +class IndentWrapper extends StatelessWidget { + final Widget? child; + final Widget? floatingActionButton; + final List? appBarActions; + final bool? hideDrawer; + final bool? noSafeArea; + final String title; + + const IndentWrapper({super.key, this.child, required this.title, this.floatingActionButton, this.appBarActions, this.hideDrawer, this.noSafeArea}); + + @override + Widget build(BuildContext context) { + final content = child ?? Container(); + + return Scaffold( + appBar: AppBar(title: Text(title), actions: appBarActions), + floatingActionButton: floatingActionButton, + drawer: (hideDrawer ?? false) ? null : const SolianNavigationDrawer(), + body: (noSafeArea ?? false) ? content : SafeArea(child: content), + ); + } +} diff --git a/lib/widgets/posts/attachment_editor.dart b/lib/widgets/posts/attachment_editor.dart new file mode 100755 index 0000000..7d9ca1e --- /dev/null +++ b/lib/widgets/posts/attachment_editor.dart @@ -0,0 +1,300 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math' as math; + +import 'package:crypto/crypto.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:provider/provider.dart'; +import 'package:solian/models/post.dart'; +import 'package:solian/providers/auth.dart'; +import 'package:solian/utils/service_url.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class AttachmentEditor extends StatefulWidget { + final List current; + final void Function(List data) onUpdate; + + const AttachmentEditor( + {super.key, required this.current, required this.onUpdate}); + + @override + State createState() => _AttachmentEditorState(); +} + +class _AttachmentEditorState extends State { + final _imagePicker = ImagePicker(); + + bool isSubmitting = false; + + List _attachments = List.empty(growable: true); + + void viewAttachMethods(BuildContext context) { + showModalBottomSheet( + context: context, + builder: (context) => AttachmentEditorMethodPopup( + pickImage: () => pickImageToUpload(context), + ), + ); + } + + Future pickImageToUpload(BuildContext context) async { + final auth = context.read(); + if (!await auth.isAuthorized()) return; + + final image = await _imagePicker.pickImage(source: ImageSource.gallery); + if (image == null) return; + + setState(() => isSubmitting = true); + + final file = File(image.path); + final hashcode = await calculateSha256(file); + + if (Navigator.canPop(context)) { + Navigator.pop(context); + } + + try { + await uploadAttachment(file, hashcode); + } catch (err) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Something went wrong... $err")), + ); + } finally { + setState(() => isSubmitting = false); + } + } + + Future uploadAttachment(File file, String hashcode) async { + final auth = context.read(); + final req = MultipartRequest( + 'POST', + getRequestUri('interactive', '/api/attachments'), + ); + req.files.add(await MultipartFile.fromPath('attachment', file.path)); + req.fields['hashcode'] = hashcode; + + var res = await auth.client!.send(req); + print(res); + if (res.statusCode == 200) { + var result = Attachment.fromJson( + jsonDecode(utf8.decode(await res.stream.toBytes()))["info"], + ); + setState(() => _attachments.add(result)); + widget.onUpdate(_attachments); + } else { + throw Exception(utf8.decode(await res.stream.toBytes())); + } + } + + Future disposeAttachment( + BuildContext context, Attachment item, int index) async { + final auth = context.read(); + + final req = MultipartRequest( + 'DELETE', + getRequestUri('interactive', '/api/attachments/${item.id}'), + ); + + setState(() => isSubmitting = true); + var res = await auth.client!.send(req); + if (res.statusCode == 200) { + setState(() => _attachments.removeAt(index)); + widget.onUpdate(_attachments); + } else { + final err = utf8.decode(await res.stream.toBytes()); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Something went wrong... $err")), + ); + } + setState(() => isSubmitting = false); + } + + Future calculateSha256(File file) async { + final bytes = await file.readAsBytes(); + final digest = sha256.convert(bytes); + return digest.toString(); + } + + String getFileName(Attachment item) { + return item.filename.replaceAll(RegExp(r'\.[^/.]+$'), ''); + } + + String getFileType(Attachment item) { + switch (item.type) { + case 1: + return 'Photo'; + case 2: + return 'Video'; + case 3: + return 'Audio'; + default: + return 'Others'; + } + } + + String formatBytes(int bytes, {int decimals = 2}) { + if (bytes == 0) return '0 Bytes'; + const k = 1024; + final dm = decimals < 0 ? 0 : decimals; + final sizes = [ + 'Bytes', + 'KiB', + 'MiB', + 'GiB', + 'TiB', + 'PiB', + 'EiB', + 'ZiB', + 'YiB' + ]; + final i = (math.log(bytes) / math.log(k)).floor().toInt(); + return '${(bytes / math.pow(k, i)).toStringAsFixed(dm)} ${sizes[i]}'; + } + + @override + void initState() { + _attachments = widget.current; + super.initState(); + } + + @override + Widget build(BuildContext context) { + final auth = context.read(); + + return Column( + children: [ + Container( + padding: const EdgeInsets.only(left: 8, right: 8, top: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 12.0, + ), + child: Text( + AppLocalizations.of(context)!.attachment, + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + FutureBuilder( + future: auth.isAuthorized(), + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data == true) { + return TextButton( + onPressed: isSubmitting + ? null + : () => viewAttachMethods(context), + style: TextButton.styleFrom(shape: const CircleBorder()), + child: const Icon(Icons.add_circle), + ); + } else { + return Container(); + } + }, + ), + ], + ), + ), + isSubmitting ? const LinearProgressIndicator() : Container(), + Expanded( + child: ListView.separated( + itemCount: _attachments.length, + itemBuilder: (context, index) { + var element = _attachments[index]; + return Container( + padding: const EdgeInsets.only(left: 16, right: 8), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + getFileName(element), + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + "${getFileType(element)} · ${formatBytes(element.filesize)}", + ), + ], + ), + ), + TextButton( + style: TextButton.styleFrom( + shape: const CircleBorder(), + foregroundColor: Colors.red, + ), + child: const Icon(Icons.delete), + onPressed: () => + disposeAttachment(context, element, index), + ), + ], + ), + ); + }, + separatorBuilder: (context, index) => const Divider(), + ), + ), + ], + ); + } +} + +class AttachmentEditorMethodPopup extends StatelessWidget { + final Function pickImage; + + const AttachmentEditorMethodPopup({super.key, required this.pickImage}); + + @override + Widget build(BuildContext context) { + return Container( + height: 320, + padding: const EdgeInsets.only(left: 8, right: 8, top: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 12.0, + ), + child: Text( + AppLocalizations.of(context)!.attachmentAdd, + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () => pickImage(), + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.add_photo_alternate, + color: Colors.indigo), + const SizedBox(height: 8), + Text(AppLocalizations.of(context)!.pickPhoto), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/posts/item.dart b/lib/widgets/posts/item.dart index 816b21b..8b9c0a7 100644 --- a/lib/widgets/posts/item.dart +++ b/lib/widgets/posts/item.dart @@ -80,7 +80,7 @@ class _PostItemState extends State { children: [ ...headingParts, Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), + padding: const EdgeInsets.only(left: 12, right: 12, top: 4), child: renderContent(), ), renderAttachments(), diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 71fe128..11c48a5 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,11 +6,15 @@ #include "generated_plugin_registrant.h" +#include #include #include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 4ab696d..ba9d398 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux flutter_secure_storage_linux media_kit_video url_launcher_linux diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 0e54e39..e566c73 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import file_selector_macos import flutter_secure_storage_macos import media_kit_video import package_info_plus @@ -15,6 +16,7 @@ import video_player_avfoundation import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) diff --git a/pubspec.lock b/pubspec.lock index f01a978..ce3494c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,8 +73,16 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" - crypto: + cross_file: dependency: transitive + description: + name: cross_file + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" + url: "https://pub.dev" + source: hosted + version: "0.3.4+1" + crypto: + dependency: "direct main" description: name: crypto sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab @@ -121,6 +129,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.dev" + source: hosted + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 + url: "https://pub.dev" + source: hosted + version: "0.9.3+3" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.dev" + source: hosted + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 + url: "https://pub.dev" + source: hosted + version: "0.9.3+1" fixnum: dependency: transitive description: @@ -163,6 +203,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.22+1" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + url: "https://pub.dev" + source: hosted + version: "2.0.19" flutter_secure_storage: dependency: "direct main" description: @@ -269,6 +317,70 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.7" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "1f498d086203360cca099d20ffea2963f48c39ce91bdd8a3b6d4a045786b02c8" + url: "https://pub.dev" + source: hosted + version: "1.0.8" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "844c6da4e4f2829dffdab97816bca09d0e0977e8dcef7450864aba4e07967a58" + url: "https://pub.dev" + source: hosted + version: "0.8.9+6" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "6a1704fdd75022272e7e7a897a9068e9c2ff3cd6a66820bf3ded810633eac954" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: "917a5cadd67d052554cfb258595e54217de53fac5b52939426e26319a02e6297" + url: "https://pub.dev" + source: hosted + version: "0.8.9+2" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80" + url: "https://pub.dev" + source: hosted + version: "2.10.0" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" infinite_scroll_pagination: dependency: "direct main" description: @@ -381,6 +493,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.5" nested: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9a35d62..75e6ded 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,8 @@ dependencies: flutter_secure_storage: ^9.0.0 oauth2: ^2.0.2 webview_flutter: ^4.7.0 + crypto: ^3.0.3 + image_picker: ^1.0.8 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index c38fbf3..bfc7d59 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,12 +6,15 @@ #include "generated_plugin_registrant.h" +#include #include #include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); MediaKitVideoPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 827eb9c..d932f37 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows flutter_secure_storage_windows media_kit_video screen_brightness_windows