mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-10-31 03:57:27 +01:00 
			
		
		
		
	feat: Implement logger
This commit is contained in:
		
							parent
							
								
									e8025931e5
								
							
						
					
					
						commit
						714c7b41dd
					
				| @ -15,6 +15,7 @@ import 'package:flushbar/flushbar.dart'; | ||||
| import 'package:flutter/foundation.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/l10n.dart'; | ||||
| import 'package:logger_flutter/logger_flutter.dart'; | ||||
| import 'package:universal_html/prefer_universal/html.dart' as html; | ||||
| import 'package:url_launcher/url_launcher.dart'; | ||||
| import 'package:path_provider/path_provider.dart'; | ||||
| @ -166,7 +167,7 @@ class MatrixState extends State<Matrix> { | ||||
|           ), | ||||
|         ); | ||||
|       default: | ||||
|         debugPrint('Warning! Cannot handle the stage "$stage"'); | ||||
|         Logs().w('Warning! Cannot handle the stage "$stage"'); | ||||
|         return; | ||||
|     } | ||||
|   } | ||||
| @ -301,13 +302,13 @@ class MatrixState extends State<Matrix> { | ||||
|       } | ||||
|       final configJson = json.decode(configJsonString); | ||||
|       AppConfig.loadFromJson(configJson); | ||||
|     } catch (error) { | ||||
|       debugPrint( | ||||
|           '[ConfigLoader] Failed to load config.json: ' + error.toString()); | ||||
|     } catch (e, s) { | ||||
|       Logs().w('[ConfigLoader] Failed to load config.json', e, s); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   void initMatrix() { | ||||
|     LogConsole.init(); | ||||
|     clientName = | ||||
|         '${AppConfig.applicationName} ${kIsWeb ? 'Web' : Platform.operatingSystem}'; | ||||
|     final Set verificationMethods = <KeyVerificationMethod>{ | ||||
|  | ||||
| @ -1757,6 +1757,11 @@ | ||||
|       "unreadChats": {} | ||||
|     } | ||||
|   }, | ||||
|   "yourPublicKey": "Your public key", | ||||
|   "@yourPublicKey": { | ||||
|     "type": "text", | ||||
|     "placeholders": {} | ||||
|   }, | ||||
|   "numUsersTyping": "{count} users are typing…", | ||||
|   "@numUsersTyping": { | ||||
|     "type": "text", | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| // @dart=2.9 | ||||
| import 'dart:async'; | ||||
| 
 | ||||
| import 'package:famedlysdk/famedlysdk.dart'; | ||||
|  | ||||
| @ -5,7 +5,6 @@ import 'package:famedlysdk/famedlysdk.dart'; | ||||
| import 'package:path_provider/path_provider.dart'; | ||||
| import 'package:sqflite/sqflite.dart' show getDatabasesPath; | ||||
| import 'package:path/path.dart' as p; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:moor/moor.dart'; | ||||
| import 'package:moor/isolate.dart'; | ||||
| import '../platform_infos.dart'; | ||||
| @ -53,7 +52,7 @@ Future<Database> constructDb( | ||||
|     String filename = 'database.sqlite', | ||||
|     String password = ''}) async { | ||||
|   if (PlatformInfos.isMobile || Platform.isMacOS) { | ||||
|     debugPrint('[Moor] using encrypted moor'); | ||||
|     Logs().v('[Moor] using encrypted moor'); | ||||
|     final dbFolder = await getDatabasesPath(); | ||||
|     final targetPath = p.join(dbFolder, filename); | ||||
|     final receivePort = ReceivePort(); | ||||
| @ -65,11 +64,11 @@ Future<Database> constructDb( | ||||
|     final isolate = (await receivePort.first as MoorIsolate); | ||||
|     return Database.connect(await isolate.connect()); | ||||
|   } else if (Platform.isLinux) { | ||||
|     debugPrint('[Moor] using Linux desktop moor'); | ||||
|     Logs().v('[Moor] using Linux desktop moor'); | ||||
|     final appDocDir = await getApplicationSupportDirectory(); | ||||
|     return Database(moor.VmDatabase(File('${appDocDir.path}/$filename'))); | ||||
|   } else if (Platform.isWindows) { | ||||
|     debugPrint('[Moor] using Windows desktop moor'); | ||||
|     Logs().v('[Moor] using Windows desktop moor'); | ||||
|     open.overrideFor(OperatingSystem.windows, _openOnWindows); | ||||
|     return Database(moor.VmDatabase.memory()); | ||||
|   } | ||||
|  | ||||
| @ -1,13 +1,12 @@ | ||||
| import 'package:famedlysdk/famedlysdk.dart'; | ||||
| import 'package:moor/moor_web.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'dart:html'; | ||||
| 
 | ||||
| Future<Database> constructDb( | ||||
|     {bool logStatements = false, | ||||
|     String filename = 'database.sqlite', | ||||
|     String password = ''}) async { | ||||
|   debugPrint('[Moor] Using moor web'); | ||||
|   Logs().v('[Moor] Using moor web'); | ||||
|   return Database(WebDatabase.withStorage( | ||||
|       MoorWebStorage.indexedDbIfSupported(filename), | ||||
|       logStatements: logStatements)); | ||||
|  | ||||
| @ -54,7 +54,7 @@ abstract class FirebaseController { | ||||
|       return; | ||||
|     } | ||||
|     final pushers = await client.requestPushers().catchError((e) { | ||||
|       debugPrint('[Push] Unable to request pushers: ${e.toString()}'); | ||||
|       Logs().w('[Push] Unable to request pushers', e); | ||||
|       return <Pusher>[]; | ||||
|     }); | ||||
|     final currentPushers = pushers.where((pusher) => pusher.pushkey == token); | ||||
| @ -68,7 +68,7 @@ abstract class FirebaseController { | ||||
|             AppConfig.pushNotificationsGatewayUrl && | ||||
|         currentPushers.first.data.format == | ||||
|             AppConfig.pushNotificationsPusherFormat) { | ||||
|       debugPrint('[Push] Pusher already set'); | ||||
|       Logs().i('[Push] Pusher already set'); | ||||
|     } else { | ||||
|       if (currentPushers.isNotEmpty) { | ||||
|         for (final currentPusher in currentPushers) { | ||||
| @ -78,7 +78,7 @@ abstract class FirebaseController { | ||||
|             currentPusher, | ||||
|             append: true, | ||||
|           ); | ||||
|           debugPrint('[Push] Remove legacy pusher for this device'); | ||||
|           Logs().i('[Push] Remove legacy pusher for this device'); | ||||
|         } | ||||
|       } | ||||
|       await client | ||||
| @ -97,8 +97,8 @@ abstract class FirebaseController { | ||||
|         ), | ||||
|         append: false, | ||||
|       ) | ||||
|           .catchError((e) { | ||||
|         debugPrint('[Push] Unable to set pushers: ${e.toString()}'); | ||||
|           .catchError((e, s) { | ||||
|         Logs().e('[Push] Unable to set pushers', e, s); | ||||
|         return []; | ||||
|       }); | ||||
|     } | ||||
| @ -121,7 +121,7 @@ abstract class FirebaseController { | ||||
|       } catch (_) { | ||||
|         await FlushbarHelper.createError(message: 'Failed to open chat...') | ||||
|             .show(context); | ||||
|         debugPrint(_); | ||||
|         rethrow; | ||||
|       } | ||||
|     }; | ||||
| 
 | ||||
| @ -145,7 +145,7 @@ abstract class FirebaseController { | ||||
|       onResume: goToRoom, | ||||
|       onLaunch: goToRoom, | ||||
|     ); | ||||
|     debugPrint('[Push] Firebase initialized'); | ||||
|     Logs().i('[Push] Firebase initialized'); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
| @ -162,10 +162,10 @@ abstract class FirebaseController { | ||||
|         return null; | ||||
|       } | ||||
|       if (context != null && Matrix.of(context).activeRoomId == roomId) { | ||||
|         debugPrint('[Push] New clearing push'); | ||||
|         Logs().i('[Push] New clearing push'); | ||||
|         return null; | ||||
|       } | ||||
|       debugPrint('[Push] New message received'); | ||||
|       Logs().i('[Push] New message received'); | ||||
|       // FIXME unable to init without context currently https://github.com/flutter/flutter/issues/67092 | ||||
|       // Locked on EN until issue resolved | ||||
|       final i18n = context == null ? L10nEn() : L10n.of(context); | ||||
| @ -183,7 +183,7 @@ abstract class FirebaseController { | ||||
|         final platform = kIsWeb ? 'Web' : Platform.operatingSystem; | ||||
|         final clientName = 'FluffyChat $platform'; | ||||
|         client = Client(clientName, databaseBuilder: getDatabase)..init(); | ||||
|         debugPrint('[Push] Use a temp client'); | ||||
|         Logs().i('[Push] Use a temp client'); | ||||
|         await client.onLoginStateChanged.stream | ||||
|             .firstWhere((l) => l == LoginState.logged) | ||||
|             .timeout( | ||||
| @ -194,12 +194,12 @@ abstract class FirebaseController { | ||||
|       // Get the room | ||||
|       var room = client.getRoomById(roomId); | ||||
|       if (room == null) { | ||||
|         debugPrint('[Push] Wait for the room'); | ||||
|         Logs().i('[Push] Wait for the room'); | ||||
|         await client.onRoomUpdate.stream | ||||
|             .where((u) => u.id == roomId) | ||||
|             .first | ||||
|             .timeout(Duration(seconds: 5)); | ||||
|         debugPrint('[Push] Room found'); | ||||
|         Logs().i('[Push] Room found'); | ||||
|         room = client.getRoomById(roomId); | ||||
|         if (room == null) return null; | ||||
|       } | ||||
| @ -207,12 +207,12 @@ abstract class FirebaseController { | ||||
|       // Get the event | ||||
|       var event = await client.database.getEventById(client.id, eventId, room); | ||||
|       if (event == null) { | ||||
|         debugPrint('[Push] Wait for the event'); | ||||
|         Logs().i('[Push] Wait for the event'); | ||||
|         final eventUpdate = await client.onEvent.stream | ||||
|             .where((u) => u.content['event_id'] == eventId) | ||||
|             .first | ||||
|             .timeout(Duration(seconds: 5)); | ||||
|         debugPrint('[Push] Event found'); | ||||
|         Logs().i('[Push] Event found'); | ||||
|         event = Event.fromJson(eventUpdate.content, room); | ||||
|         if (room == null) return null; | ||||
|       } | ||||
| @ -284,11 +284,10 @@ abstract class FirebaseController { | ||||
|       if (tempClient) { | ||||
|         await client.dispose(); | ||||
|         client = null; | ||||
|         debugPrint('[Push] Temp client disposed'); | ||||
|         Logs().i('[Push] Temp client disposed'); | ||||
|       } | ||||
|     } catch (exception) { | ||||
|       debugPrint('[Push] Error while processing notification: ' + | ||||
|           exception.toString()); | ||||
|     } catch (e, s) { | ||||
|       Logs().e('[Push] Error while processing notification', e, s); | ||||
|       await _showDefaultNotification(message); | ||||
|     } | ||||
|     return null; | ||||
| @ -341,9 +340,8 @@ abstract class FirebaseController { | ||||
|       await flutterLocalNotificationsPlugin.show( | ||||
|           1, title, l10n.openAppToReadMessages, platformChannelSpecifics, | ||||
|           payload: roomID); | ||||
|     } catch (exception) { | ||||
|       debugPrint('[Push] Error while processing background notification: ' + | ||||
|           exception.toString()); | ||||
|     } catch (e, s) { | ||||
|       Logs().e('[Push] Error while processing background notification', e, s); | ||||
|     } | ||||
|     return Future<void>.value(); | ||||
|   } | ||||
| @ -374,7 +372,7 @@ abstract class FirebaseController { | ||||
|         IosNotificationSettings(sound: true, badge: true, alert: true)); | ||||
|     _firebaseMessaging.onIosSettingsRegistered | ||||
|         .listen((IosNotificationSettings settings) { | ||||
|       debugPrint('Settings registered: $settings'); | ||||
|       Logs().i('Settings registered: $settings'); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import 'package:adaptive_dialog/adaptive_dialog.dart'; | ||||
| import 'package:famedlysdk/famedlysdk.dart'; | ||||
| import 'package:flushbar/flushbar_helper.dart'; | ||||
| import 'package:fluffychat/app_config.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| @ -37,8 +38,7 @@ abstract class SentryController { | ||||
|   static final sentry = SentryClient(dsn: AppConfig.sentryDns); | ||||
| 
 | ||||
|   static void captureException(error, stackTrace) async { | ||||
|     debugPrint(error.toString()); | ||||
|     debugPrint(stackTrace.toString()); | ||||
|     Logs().e('Capture exception', error, stackTrace); | ||||
|     if (!kDebugMode && await getSentryStatus()) { | ||||
|       await sentry.captureException( | ||||
|         exception: error, | ||||
|  | ||||
| @ -1,73 +0,0 @@ | ||||
| import 'package:fluffychat/components/adaptive_page_layout.dart'; | ||||
| import 'package:fluffychat/components/matrix.dart'; | ||||
| import 'package:fluffychat/utils/beautify_string_extension.dart'; | ||||
| import 'package:fluffychat/views/chat_list.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/l10n.dart'; | ||||
| import 'package:olm/olm.dart' as olm; | ||||
| 
 | ||||
| class AppInfoView extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return AdaptivePageLayout( | ||||
|       primaryPage: FocusPage.SECOND, | ||||
|       firstScaffold: ChatList(), | ||||
|       secondScaffold: AppInfo(), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class AppInfo extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     var client = Matrix.of(context).client; | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: Text(L10n.of(context).accountInformation), | ||||
|       ), | ||||
|       body: ListView( | ||||
|         children: <Widget>[ | ||||
|           ListTile( | ||||
|             title: Text(L10n.of(context).yourOwnUsername + ':'), | ||||
|             subtitle: Text(client.userID), | ||||
|           ), | ||||
|           ListTile( | ||||
|             title: Text('Homeserver:'), | ||||
|             subtitle: Text(client.homeserver.toString()), | ||||
|           ), | ||||
|           ListTile( | ||||
|             title: Text('Device name:'), | ||||
|             subtitle: Text(client.userDeviceKeys[client.userID] | ||||
|                     ?.deviceKeys[client.deviceID]?.deviceDisplayName ?? | ||||
|                 L10n.of(context).unknownDevice), | ||||
|           ), | ||||
|           ListTile( | ||||
|             title: Text('Device ID:'), | ||||
|             subtitle: Text(client.deviceID), | ||||
|           ), | ||||
|           ListTile( | ||||
|             title: Text('Encryption enabled:'), | ||||
|             subtitle: Text(client.encryptionEnabled.toString()), | ||||
|           ), | ||||
|           if (client.encryptionEnabled) | ||||
|             Column( | ||||
|               children: <Widget>[ | ||||
|                 ListTile( | ||||
|                   title: Text('Your public fingerprint key:'), | ||||
|                   subtitle: Text(client.fingerprintKey.beautified), | ||||
|                 ), | ||||
|                 ListTile( | ||||
|                   title: Text('Your public identity key:'), | ||||
|                   subtitle: Text(client.identityKey.beautified), | ||||
|                 ), | ||||
|                 ListTile( | ||||
|                   title: Text('LibOlm version:'), | ||||
|                   subtitle: Text(olm.get_library_version().join('.')), | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -6,6 +6,7 @@ import 'package:fluffychat/views/settings_style.dart'; | ||||
| import 'package:flushbar/flushbar_helper.dart'; | ||||
| import 'package:famedlysdk/famedlysdk.dart'; | ||||
| import 'package:file_picker_cross/file_picker_cross.dart'; | ||||
| import 'package:fluffychat/utils/beautify_string_extension.dart'; | ||||
| 
 | ||||
| import 'package:fluffychat/app_config.dart'; | ||||
| import 'package:fluffychat/utils/platform_infos.dart'; | ||||
| @ -16,6 +17,7 @@ import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/l10n.dart'; | ||||
| import 'package:image_picker/image_picker.dart'; | ||||
| import 'package:url_launcher/url_launcher.dart'; | ||||
| import 'package:logger_flutter/logger_flutter.dart'; | ||||
| 
 | ||||
| import '../components/adaptive_page_layout.dart'; | ||||
| import '../components/content_banner.dart'; | ||||
| @ -24,7 +26,6 @@ import '../components/matrix.dart'; | ||||
| import '../utils/app_route.dart'; | ||||
| import '../app_config.dart'; | ||||
| import '../config/setting_keys.dart'; | ||||
| import 'app_info.dart'; | ||||
| import 'chat_list.dart'; | ||||
| import 'settings_emotes.dart'; | ||||
| 
 | ||||
| @ -433,16 +434,6 @@ class _SettingsState extends State<Settings> { | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             ListTile( | ||||
|               trailing: Icon(Icons.account_circle_outlined), | ||||
|               title: Text(L10n.of(context).accountInformation), | ||||
|               onTap: () => Navigator.of(context).push( | ||||
|                 AppRoute.defaultRoute( | ||||
|                   context, | ||||
|                   AppInfoView(), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             ListTile( | ||||
|               trailing: Icon(Icons.bug_report_outlined), | ||||
|               title: Text(L10n.of(context).sendBugReports), | ||||
| @ -450,7 +441,7 @@ class _SettingsState extends State<Settings> { | ||||
|             ), | ||||
|             Divider(thickness: 1), | ||||
|             ListTile( | ||||
|               trailing: Icon(Icons.vpn_key_outlined), | ||||
|               trailing: Icon(Icons.security_outlined), | ||||
|               title: Text( | ||||
|                 L10n.of(context).changePassword, | ||||
|               ), | ||||
| @ -589,6 +580,15 @@ class _SettingsState extends State<Settings> { | ||||
|                 } | ||||
|               }, | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: Text(L10n.of(context).yourPublicKey), | ||||
|               onTap: () => showOkAlertDialog( | ||||
|                 context: context, | ||||
|                 title: L10n.of(context).yourPublicKey, | ||||
|                 message: client.fingerprintKey.beautified, | ||||
|               ), | ||||
|               trailing: Icon(Icons.vpn_key_outlined), | ||||
|             ), | ||||
|             Divider(thickness: 1), | ||||
|             ListTile( | ||||
|               title: Text( | ||||
| @ -598,6 +598,14 @@ class _SettingsState extends State<Settings> { | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                 ), | ||||
|               ), | ||||
|               onTap: () => Navigator.of(context).push( | ||||
|                 AppRoute.defaultRoute( | ||||
|                   context, | ||||
|                   LogConsole( | ||||
|                     showCloseButton: true, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             ListTile( | ||||
|               trailing: Icon(Icons.help_outlined), | ||||
|  | ||||
| @ -59,7 +59,6 @@ class _EmotesSettingsState extends State<EmotesSettings> { | ||||
|     if (readonly) { | ||||
|       return; | ||||
|     } | ||||
|     debugPrint('Saving....'); | ||||
|     final client = Matrix.of(context).client; | ||||
|     // be sure to preserve any data not in "short" | ||||
|     Map<String, dynamic> content; | ||||
| @ -72,7 +71,6 @@ class _EmotesSettingsState extends State<EmotesSettings> { | ||||
|       content = client.accountData['im.ponies.user_emotes']?.content ?? | ||||
|           <String, dynamic>{}; | ||||
|     } | ||||
|     debugPrint(content.toString()); | ||||
|     if (!(content['emoticons'] is Map)) { | ||||
|       content['emoticons'] = <String, dynamic>{}; | ||||
|     } | ||||
| @ -95,7 +93,6 @@ class _EmotesSettingsState extends State<EmotesSettings> { | ||||
|     } | ||||
|     // remove the old "short" key | ||||
|     content.remove('short'); | ||||
|     debugPrint(content.toString()); | ||||
|     if (widget.room != null) { | ||||
|       await SimpleDialogs(context).tryRequestWithLoadingDialog( | ||||
|         client.sendState(widget.room.id, 'im.ponies.room_emotes', content, | ||||
|  | ||||
| @ -89,7 +89,6 @@ class _SignUpPasswordState extends State<SignUpPassword> { | ||||
|         return setState(() => loading = false); | ||||
|       } | ||||
|     } catch (exception) { | ||||
|       debugPrint(exception); | ||||
|       setState(() => passwordError = exception.toString()); | ||||
|       return setState(() => loading = false); | ||||
|     } | ||||
|  | ||||
							
								
								
									
										30
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								pubspec.lock
									
									
									
									
									
								
							| @ -36,13 +36,6 @@ packages: | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.1.2" | ||||
|   ansicolor: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: ansicolor | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.1.1" | ||||
|   args: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @ -202,7 +195,7 @@ packages: | ||||
|     description: | ||||
|       path: "." | ||||
|       ref: main | ||||
|       resolved-ref: "59d5849acc2d165fef6b96bac21ec5073c939778" | ||||
|       resolved-ref: efadef4f3ea2b11d1d2b78c4bcaad50250a643e4 | ||||
|       url: "https://gitlab.com/famedly/famedlysdk.git" | ||||
|     source: git | ||||
|     version: "0.0.1" | ||||
| @ -527,6 +520,20 @@ packages: | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.0.3+6" | ||||
|   logger: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: logger | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.7.0" | ||||
|   logger_flutter: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: logger_flutter | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.7.1" | ||||
|   logging: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @ -851,6 +858,13 @@ packages: | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.6" | ||||
|   sensors: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: sensors | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.4.2+6" | ||||
|   sentry: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|  | ||||
| @ -64,6 +64,7 @@ dependencies: | ||||
|   flutter_cache_manager: ^2.0.0 | ||||
|   open_noti_settings: ^0.0.4 | ||||
|   emoji_picker: ^0.1.0 | ||||
|   logger_flutter: ^0.7.1 | ||||
| 
 | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Christian Pauly
						Christian Pauly