From 8e0441e224a7ca9557da7c304cd9014fd2367bcb Mon Sep 17 00:00:00 2001 From: Ellet <73608287+freshtechtips@users.noreply.github.com> Date: Tue, 7 Nov 2023 00:48:10 +0300 Subject: [PATCH] New patch --- CHANGELOG.md | 4 + analysis_options.yaml | 59 ++++--- example/analysis_options.yaml | 57 ++++--- example/android/app/build.gradle | 14 +- .../android/app/src/main/AndroidManifest.xml | 2 - example/android/build.gradle | 4 +- example/android/gradle.properties | 3 - .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/assets/sample_data_nomedia.json | 37 ----- example/lib/main.dart | 16 +- example/lib/pages/home_page.dart | 154 ++++++++++++++++-- example/lib/pages/read_only_page.dart | 2 + example/lib/widgets/demo_scaffold.dart | 4 +- .../lib/widgets/time_stamp_embed_widget.dart | 4 +- example/pubspec.yaml | 13 +- example/test/widget_test.dart | 2 +- flutter_quill_extensions/CHANGELOG.md | 3 + flutter_quill_extensions/LICENSE | 2 +- flutter_quill_extensions/README.md | 2 +- .../analysis_options.yaml | 59 ++++--- .../lib/core/exceptions.dart | 12 -- .../lib/flutter_quill_extensions.dart | 19 ++- .../lib/logic/extensions/controller.dart | 24 ++- .../models/config/shared_configurations.dart | 38 ++++- .../services/image_picker/s_image_picker.dart | 1 + .../services/image_saver/s_image_saver.dart | 1 + ...uill_utils.dart => quill_image_utils.dart} | 0 .../embeds/embed_types/image.dart | 2 +- flutter_quill_extensions/pubspec.yaml | 8 +- flutter_quill_test/CHANGELOG.md | 4 + flutter_quill_test/LICENSE | 2 +- flutter_quill_test/README.md | 17 ++ flutter_quill_test/analysis_options.yaml | 59 ++++--- .../lib/src/test/widget_tester_extension.dart | 34 ++-- flutter_quill_test/pubspec.yaml | 4 +- .../test/flutter_quill_test_test.dart | 1 + lib/src/core/utils/logger.dart | 68 ++++++++ lib/src/widgets/proxy.dart | 2 +- lib/src/widgets/raw_editor/raw_editor.dart | 3 +- .../widgets/style_widgets/checkbox_point.dart | 6 +- lib/src/widgets/text_line.dart | 26 +-- lib/src/widgets/toolbar/base_toolbar.dart | 8 +- .../widgets/toolbar/buttons/link_style.dart | 4 +- lib/src/widgets/utils/provider.dart | 4 + pubspec.yaml | 4 +- 45 files changed, 538 insertions(+), 256 deletions(-) delete mode 100644 flutter_quill_extensions/lib/core/exceptions.dart rename flutter_quill_extensions/lib/logic/utils/{quill_utils.dart => quill_image_utils.dart} (100%) create mode 100644 lib/src/core/utils/logger.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 87ecc8eb..a79192b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [8.2.4] +- Follow flutter best practices +- Auto focus bug fix + ## [8.2.3] - Update `README.md` diff --git a/analysis_options.yaml b/analysis_options.yaml index 8f1b96e4..f1a38172 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:lints/recommended.yaml +include: package:flutter_lints/flutter.yaml analyzer: errors: @@ -6,32 +6,31 @@ analyzer: unsafe_html: ignore linter: rules: - - always_declare_return_types - - always_put_required_named_parameters_first - - annotate_overrides - - avoid_empty_else - - avoid_escaping_inner_quotes - - avoid_print - - avoid_redundant_argument_values - - avoid_types_on_closure_parameters - - avoid_void_async - - cascade_invocations - - directives_ordering - - lines_longer_than_80_chars - - omit_local_variable_types - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_final_fields - - prefer_final_in_for_each - - prefer_final_locals - - prefer_initializing_formals - - prefer_int_literals - - prefer_interpolation_to_compose_strings - - prefer_relative_imports - - prefer_single_quotes - - sort_constructors_first - - sort_unnamed_constructors_first - - unnecessary_lambdas - - unnecessary_parenthesis - - unnecessary_string_interpolations + always_declare_return_types: true + always_put_required_named_parameters_first: true + annotate_overrides: true + avoid_empty_else: true + avoid_escaping_inner_quotes: true + avoid_print: true + avoid_redundant_argument_values: true + avoid_types_on_closure_parameters: true + avoid_void_async: true + cascade_invocations: true + directives_ordering: true + omit_local_variable_types: true + prefer_const_constructors: true + prefer_const_constructors_in_immutables: true + prefer_const_declarations: true + prefer_final_fields: true + prefer_final_in_for_each: true + prefer_final_locals: true + prefer_initializing_formals: true + prefer_int_literals: true + prefer_interpolation_to_compose_strings: true + prefer_relative_imports: true + prefer_single_quotes: true + sort_constructors_first: true + sort_unnamed_constructors_first: true + unnecessary_lambdas: true + unnecessary_parenthesis: true + unnecessary_string_interpolations: true diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 0d290213..b55e627b 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -1,28 +1,37 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml +analyzer: + errors: + undefined_prefixed_name: ignore + unsafe_html: ignore linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + always_declare_return_types: true + always_put_required_named_parameters_first: true + annotate_overrides: true + avoid_empty_else: true + avoid_escaping_inner_quotes: true + avoid_print: true + avoid_redundant_argument_values: true + avoid_types_on_closure_parameters: true + avoid_void_async: true + cascade_invocations: true + directives_ordering: true + omit_local_variable_types: true + prefer_const_constructors: true + prefer_const_constructors_in_immutables: true + prefer_const_declarations: true + prefer_final_fields: true + prefer_final_in_for_each: true + prefer_final_locals: true + prefer_initializing_formals: true + prefer_int_literals: true + prefer_interpolation_to_compose_strings: true + prefer_relative_imports: true + prefer_single_quotes: true + sort_constructors_first: true + sort_unnamed_constructors_first: true + unnecessary_lambdas: true + unnecessary_parenthesis: true + unnecessary_string_interpolations: true + library_private_types_in_public_api: false diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index b69a36c7..118ee1d9 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -24,16 +24,16 @@ if (flutterVersionName == null) { android { namespace "com.example.example" - compileSdk flutter.compileSdkVersion + compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() + jvmTarget = '1.8' } sourceSets { @@ -41,8 +41,11 @@ android { } defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" - minSdkVersion 24 + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -50,6 +53,7 @@ android { buildTypes { release { + // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index b075bde1..7285ab78 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - @@ -8,7 +7,6 @@ - _HomePageState(); } @@ -54,9 +58,6 @@ class _HomePageState extends State { try { final result = await rootBundle.loadString('assets/sample_data_testing.json'); - // final result = await rootBundle.loadString(isDesktop() - // ? 'assets/sample_data_nomedia.json' - // : 'assets/sample_data.json'); final doc = Document.fromJson(jsonDecode(result)); _controller = QuillController( document: doc, @@ -89,12 +90,32 @@ class _HomePageState extends State { ), actions: [ IconButton( - onPressed: () { - setState(() => _isReadOnly = !_isReadOnly); - }, - icon: Icon( - _isReadOnly ? Icons.lock : Icons.edit, - )), + tooltip: 'Print to log', + onPressed: () { + print( + jsonEncode(_controller.document.toDelta().toJson()), + ); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'The quill delta json has been printed to the log.', + ), + ), + ); + }, + icon: const Icon( + Icons.print, + ), + ), + IconButton( + tooltip: 'Toggle read only', + onPressed: () { + setState(() => _isReadOnly = !_isReadOnly); + }, + icon: Icon( + _isReadOnly ? Icons.lock : Icons.edit, + ), + ), IconButton( onPressed: () => _insertTimeStamp( _controller, @@ -121,7 +142,116 @@ class _HomePageState extends State { ], ), drawer: Drawer( - child: _buildMenuBar(context), + child: ListView( + children: [ + DrawerHeader( + child: IconButton( + tooltip: 'Open document by json delta', + onPressed: () async { + final scaffoldMessenger = ScaffoldMessenger.of(context); + try { + final result = await FilePicker.platform.pickFiles( + dialogTitle: 'Pick json delta', + type: FileType.custom, + allowedExtensions: ['json'], + allowMultiple: false, + ); + final file = result?.files.firstOrNull; + final filePath = file?.path; + if (file == null || filePath == null) { + return; + } + final jsonString = await XFile(filePath).readAsString(); + _controller.document = + Document.fromJson(jsonDecode(jsonString)); + } catch (e) { + print( + 'Error while loading json delta file: ${e.toString()}', + ); + scaffoldMessenger.showSnackBar( + SnackBar( + content: Text( + 'Error while loading json delta file: ${e.toString()}', + ), + ), + ); + } + }, + icon: const Icon(Icons.file_copy), + ), + ), + ListTile( + title: const Text('Load sample data'), + onTap: () async { + final scaffoldMessenger = ScaffoldMessenger.of(context); + try { + final jsonString = await rootBundle.loadString( + 'assets/sample_data.json', + ); + _controller.document = Document.fromJson( + jsonDecode(jsonString), + ); + } catch (e) { + print( + 'Error while loading json delta file: ${e.toString()}', + ); + scaffoldMessenger.showSnackBar(SnackBar( + content: Text( + 'Error while loading json delta file: ${e.toString()}', + ), + )); + } + }, + ), + ListTile( + title: const Text('Load sample data with no media'), + onTap: () async { + final scaffoldMessenger = ScaffoldMessenger.of(context); + try { + final jsonString = await rootBundle.loadString( + 'assets/sample_data_nomedia.json', + ); + _controller.document = Document.fromJson( + jsonDecode(jsonString), + ); + } catch (e) { + print( + 'Error while loading json delta file: ${e.toString()}', + ); + scaffoldMessenger.showSnackBar(SnackBar( + content: Text( + 'Error while loading json delta file: ${e.toString()}', + ), + )); + } + }, + ), + ListTile( + title: const Text('Load testing sample data '), + onTap: () async { + final scaffoldMessenger = ScaffoldMessenger.of(context); + try { + final jsonString = await rootBundle.loadString( + 'assets/sample_data_testing.json', + ); + _controller.document = Document.fromJson( + jsonDecode(jsonString), + ); + } catch (e) { + print( + 'Error while loading json delta file: ${e.toString()}', + ); + scaffoldMessenger.showSnackBar(SnackBar( + content: Text( + 'Error while loading json delta file: ${e.toString()}', + ), + )); + } + }, + ), + _buildMenuBar(context), + ], + ), ), body: _buildWelcomeEditor(context), ); @@ -541,7 +671,7 @@ class _HomePageState extends State { Navigator.push( super.context, MaterialPageRoute( - builder: (context) => ReadOnlyPage(), + builder: (context) => const ReadOnlyPage(), ), ); } diff --git a/example/lib/pages/read_only_page.dart b/example/lib/pages/read_only_page.dart index 44c85b3f..6f38741a 100644 --- a/example/lib/pages/read_only_page.dart +++ b/example/lib/pages/read_only_page.dart @@ -9,6 +9,8 @@ import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; import '../widgets/demo_scaffold.dart'; class ReadOnlyPage extends StatefulWidget { + const ReadOnlyPage({super.key}); + @override _ReadOnlyPageState createState() => _ReadOnlyPageState(); } diff --git a/example/lib/widgets/demo_scaffold.dart b/example/lib/widgets/demo_scaffold.dart index 9de4e786..85311a85 100644 --- a/example/lib/widgets/demo_scaffold.dart +++ b/example/lib/widgets/demo_scaffold.dart @@ -18,8 +18,8 @@ class DemoScaffold extends StatefulWidget { this.actions, this.showToolbar = true, this.floatingActionButton, - Key? key, - }) : super(key: key); + super.key, + }); /// Filename of the document to load into the editor. final String documentFilename; diff --git a/example/lib/widgets/time_stamp_embed_widget.dart b/example/lib/widgets/time_stamp_embed_widget.dart index 968cb441..d78457f0 100644 --- a/example/lib/widgets/time_stamp_embed_widget.dart +++ b/example/lib/widgets/time_stamp_embed_widget.dart @@ -21,8 +21,8 @@ class TimeStampEmbedBuilderWidget extends EmbedBuilder { String get key => 'timeStamp'; @override - String toPlainText(Embed embed) { - return embed.value.data; + String toPlainText(Embed node) { + return node.value.data; } @override diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 0067ce4a..c9db1b46 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -9,22 +9,29 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter universal_html: ^2.2.4 cupertino_icons: ^1.0.6 path_provider: ^2.1.1 # filesystem_picker: ^4.0.0 - # file_picker: ^6.1.1 - flutter_quill: ^8.2.2 - flutter_quill_extensions: ^0.6.0-dev.5 + file_picker: ^6.1.1 + flutter_quill: ^8.2.3 + flutter_quill_extensions: ^0.6.1 + path: ^1.8.3 dependency_overrides: flutter_quill: path: ../ flutter_quill_extensions: path: ../flutter_quill_extensions + flutter_quill_test: + path: ../flutter_quill_test dev_dependencies: + flutter_lints: ^3.0.1 + flutter_quill_test: ^0.0.3 flutter_test: sdk: flutter diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index ca3ecf17..5f46835d 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -12,7 +12,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Counter increments smoke test', (tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); diff --git a/flutter_quill_extensions/CHANGELOG.md b/flutter_quill_extensions/CHANGELOG.md index 591acd85..3754c611 100644 --- a/flutter_quill_extensions/CHANGELOG.md +++ b/flutter_quill_extensions/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.6.2 +- Add more default exports + ## 0.6.1 - Fix bug on web that causing the project to not build diff --git a/flutter_quill_extensions/LICENSE b/flutter_quill_extensions/LICENSE index aaec6d89..e82b91ed 100644 --- a/flutter_quill_extensions/LICENSE +++ b/flutter_quill_extensions/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Xin Yao +Copyright (c) 2023 Flutter Quill Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/flutter_quill_extensions/README.md b/flutter_quill_extensions/README.md index 3078767c..c59ae175 100644 --- a/flutter_quill_extensions/README.md +++ b/flutter_quill_extensions/README.md @@ -57,7 +57,7 @@ dependencies: ## Usage -Before starting using this package you must follow the setup +Before starting using this package you must follow the [setup](#installation) Set the `embedBuilders` and `embedToolbar` params in configurations of `QuillEditor` and `QuillToolbar` with the values provided by this repository. diff --git a/flutter_quill_extensions/analysis_options.yaml b/flutter_quill_extensions/analysis_options.yaml index 8f1b96e4..f1a38172 100644 --- a/flutter_quill_extensions/analysis_options.yaml +++ b/flutter_quill_extensions/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:lints/recommended.yaml +include: package:flutter_lints/flutter.yaml analyzer: errors: @@ -6,32 +6,31 @@ analyzer: unsafe_html: ignore linter: rules: - - always_declare_return_types - - always_put_required_named_parameters_first - - annotate_overrides - - avoid_empty_else - - avoid_escaping_inner_quotes - - avoid_print - - avoid_redundant_argument_values - - avoid_types_on_closure_parameters - - avoid_void_async - - cascade_invocations - - directives_ordering - - lines_longer_than_80_chars - - omit_local_variable_types - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_final_fields - - prefer_final_in_for_each - - prefer_final_locals - - prefer_initializing_formals - - prefer_int_literals - - prefer_interpolation_to_compose_strings - - prefer_relative_imports - - prefer_single_quotes - - sort_constructors_first - - sort_unnamed_constructors_first - - unnecessary_lambdas - - unnecessary_parenthesis - - unnecessary_string_interpolations + always_declare_return_types: true + always_put_required_named_parameters_first: true + annotate_overrides: true + avoid_empty_else: true + avoid_escaping_inner_quotes: true + avoid_print: true + avoid_redundant_argument_values: true + avoid_types_on_closure_parameters: true + avoid_void_async: true + cascade_invocations: true + directives_ordering: true + omit_local_variable_types: true + prefer_const_constructors: true + prefer_const_constructors_in_immutables: true + prefer_const_declarations: true + prefer_final_fields: true + prefer_final_in_for_each: true + prefer_final_locals: true + prefer_initializing_formals: true + prefer_int_literals: true + prefer_interpolation_to_compose_strings: true + prefer_relative_imports: true + prefer_single_quotes: true + sort_constructors_first: true + sort_unnamed_constructors_first: true + unnecessary_lambdas: true + unnecessary_parenthesis: true + unnecessary_string_interpolations: true diff --git a/flutter_quill_extensions/lib/core/exceptions.dart b/flutter_quill_extensions/lib/core/exceptions.dart deleted file mode 100644 index 3967adb7..00000000 --- a/flutter_quill_extensions/lib/core/exceptions.dart +++ /dev/null @@ -1,12 +0,0 @@ -// import 'package:meta/meta.dart'; - -// @immutable -// class NetworkException implements Exception { -// const NetworkException({required this.message}); - -// final String message; - -// @override -// String toString() => -// 'Error while loading something from the network: $message'; -// } diff --git a/flutter_quill_extensions/lib/flutter_quill_extensions.dart b/flutter_quill_extensions/lib/flutter_quill_extensions.dart index a5c372ef..666f9177 100644 --- a/flutter_quill_extensions/lib/flutter_quill_extensions.dart +++ b/flutter_quill_extensions/lib/flutter_quill_extensions.dart @@ -27,10 +27,15 @@ import 'presentation/models/config/toolbar/buttons/image.dart'; import 'presentation/models/config/toolbar/buttons/media_button.dart'; import 'presentation/models/config/toolbar/buttons/video.dart'; +export '/logic/extensions/controller.dart'; export '/presentation/models/config/editor/webview.dart'; -export './logic/extensions/controller.dart'; +export 'logic/models/config/shared_configurations.dart'; +export 'presentation/embeds/editor/image/image.dart'; export 'presentation/embeds/editor/image/image_web.dart'; export 'presentation/embeds/editor/unknown.dart'; +export 'presentation/embeds/editor/video/video.dart'; +export 'presentation/embeds/editor/video/video_web.dart'; +export 'presentation/embeds/editor/webview.dart'; export 'presentation/embeds/embed_types.dart'; export 'presentation/embeds/embed_types/image.dart'; export 'presentation/embeds/embed_types/video.dart'; @@ -42,6 +47,11 @@ export 'presentation/embeds/toolbar/utils/image_video_utils.dart'; export 'presentation/embeds/toolbar/video_button/video_button.dart'; export 'presentation/embeds/utils.dart'; export 'presentation/models/config/editor/image/image.dart'; +// TODO: Temporary +// ignore: unused_import +export 'presentation/models/config/editor/image/image_web.dart'; +export 'presentation/models/config/editor/video/video.dart'; +export 'presentation/models/config/editor/video/video_web.dart'; export 'presentation/models/config/toolbar/buttons/camera.dart'; export 'presentation/models/config/toolbar/buttons/formula.dart'; export 'presentation/models/config/toolbar/buttons/image.dart'; @@ -111,9 +121,12 @@ class FlutterQuillEmbeds { /// Returns a list of embed builders specifically designed for web support. /// - /// [QuillEditorImageEmbedBuilderWeb] is the embed builder for handling + /// [QuillEditorWebImageEmbedBuilder] is the embed builder for handling /// images on the web. /// + /// [QuillEditorWebVideoEmbedBuilder] is the embed builder for handling + /// videos iframe on the web. + /// static List editorsWebBuilders( {QuillEditorWebImageEmbedConfigurations? imageEmbedConfigurations = const QuillEditorWebImageEmbedConfigurations(), @@ -122,7 +135,7 @@ class FlutterQuillEmbeds { if (!kIsWeb) { throw UnsupportedError( 'The editorsWebBuilders() is only for web, please use editorBuilders() ' - 'instead', + 'instead for other platforms', ); } return [ diff --git a/flutter_quill_extensions/lib/logic/extensions/controller.dart b/flutter_quill_extensions/lib/logic/extensions/controller.dart index 2318e5f6..234c418e 100644 --- a/flutter_quill_extensions/lib/logic/extensions/controller.dart +++ b/flutter_quill_extensions/lib/logic/extensions/controller.dart @@ -2,9 +2,16 @@ import 'package:flutter_quill/flutter_quill.dart'; import '../../presentation/embeds/editor/webview.dart'; +/// Extension functions on [QuillController] +/// that make it easier to insert the embed blocks +/// +/// and provide some other extra utilities extension QuillControllerExt on QuillController { int get index => selection.baseOffset; int get length => selection.extentOffset - index; + + /// Insert webview embed block, it requires [initialUrl] to load + /// the initial page void insertWebViewBlock({ required String initialUrl, }) { @@ -24,19 +31,32 @@ extension QuillControllerExt on QuillController { ); } + /// Insert image embed block, it requires the [imageSource] + /// + /// it could be local image on the system file + /// http image on the network + /// + /// image base 64 void insertImageBlock({ - required String imageUrl, + required String imageSource, }) { this ..skipRequestKeyboard = true ..replaceText( index, length, - BlockEmbed.image(imageUrl), + BlockEmbed.image(imageSource), null, ); } + /// Insert video embed block, it requires the [videoUrl] + /// + /// it could be the video url directly (.mp4) + /// either locally on file system + /// or http video on the network + /// + /// it also supports youtube video url void insertVideoBlock({ required String videoUrl, }) { diff --git a/flutter_quill_extensions/lib/logic/models/config/shared_configurations.dart b/flutter_quill_extensions/lib/logic/models/config/shared_configurations.dart index 307be978..6b4075a9 100644 --- a/flutter_quill_extensions/lib/logic/models/config/shared_configurations.dart +++ b/flutter_quill_extensions/lib/logic/models/config/shared_configurations.dart @@ -5,6 +5,33 @@ import 'package:meta/meta.dart' show immutable; import '../../services/image_picker/s_image_picker.dart'; import '../../services/image_saver/s_image_saver.dart'; +/// Configurations for Flutter Quill Extensions +/// that is shared between the toolbar and editor for the extensions package +/// +/// Example on how to setup it: +/// +/// ```dart +/// QuillProvider( +/// configurations: QuillConfigurations( +/// sharedConfigurations: const QuillSharedConfigurations( +/// extraConfigurations: { +/// QuillSharedExtensionsConfigurations.key: +/// QuillSharedExtensionsConfigurations( +/// // Feel free to explore it +/// ), +/// }, +/// ), +/// controller: _controller, +/// ), +/// child: const Column( +/// children: [ +/// // QuillToolbar +/// // QuillEditor +/// // ... +/// ], +// ), +/// ) +/// ``` @immutable class QuillSharedExtensionsConfigurations { const QuillSharedExtensionsConfigurations({ @@ -33,11 +60,18 @@ class QuillSharedExtensionsConfigurations { return const QuillSharedExtensionsConfigurations(); } + /// The key to be used in the `extraConfigurations` property + /// which can be found in the [QuillSharedConfigurations] + /// which lives in the [QuillConfigurations] + /// + /// which exists in the [QuillProvider] static const String key = 'quillSharedExtensionsConfigurations'; - /// Default to [ImagePickerService.defaultImpl] + /// Defaults to [ImagePickerService.defaultImpl] final ImagePickerService? _imagePickerService; + /// A getter method which returns the [ImagePickerService] that is provided + /// by the developer, if it can't be found then we will use default impl ImagePickerService get imagePickerService { return _imagePickerService ?? ImagePickerService.defaultImpl(); } @@ -45,6 +79,8 @@ class QuillSharedExtensionsConfigurations { /// Default to [ImageSaverService.defaultImpl] final ImageSaverService? _imageSaverService; + /// A getter method which returns the [ImageSaverService] that is provided + /// by the developer, if it can't be found then we will use default impl ImageSaverService get imageSaverService { return _imageSaverService ?? ImageSaverService.defaultImpl(); } diff --git a/flutter_quill_extensions/lib/logic/services/image_picker/s_image_picker.dart b/flutter_quill_extensions/lib/logic/services/image_picker/s_image_picker.dart index 9b9a43e4..23d19e2e 100644 --- a/flutter_quill_extensions/lib/logic/services/image_picker/s_image_picker.dart +++ b/flutter_quill_extensions/lib/logic/services/image_picker/s_image_picker.dart @@ -1,6 +1,7 @@ import 'image_picker.dart'; import 'packages/image_picker.dart'; +/// A service used for packing images in the extensions package class ImagePickerService extends ImagePickerInterface { const ImagePickerService( this._impl, diff --git a/flutter_quill_extensions/lib/logic/services/image_saver/s_image_saver.dart b/flutter_quill_extensions/lib/logic/services/image_saver/s_image_saver.dart index f11587fd..35b9ee34 100644 --- a/flutter_quill_extensions/lib/logic/services/image_saver/s_image_saver.dart +++ b/flutter_quill_extensions/lib/logic/services/image_saver/s_image_saver.dart @@ -2,6 +2,7 @@ import 'image_saver.dart'; import 'packages/gal.dart' show ImageSaverGalImpl; +/// A service used for saving images in the extensions package class ImageSaverService extends ImageSaverInterface { final ImageSaverInterface _impl; const ImageSaverService(this._impl); diff --git a/flutter_quill_extensions/lib/logic/utils/quill_utils.dart b/flutter_quill_extensions/lib/logic/utils/quill_image_utils.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/utils/quill_utils.dart rename to flutter_quill_extensions/lib/logic/utils/quill_image_utils.dart diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart b/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart index 0a10ba6a..c53796ef 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart @@ -30,7 +30,7 @@ OnImageInsertCallback defaultOnImageInsertCallback() { return (imageUrl, controller) async { controller ..skipRequestKeyboard = true - ..insertImageBlock(imageUrl: imageUrl); + ..insertImageBlock(imageSource: imageUrl); }; } diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index ecbea806..12995793 100644 --- a/flutter_quill_extensions/pubspec.yaml +++ b/flutter_quill_extensions/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill_extensions description: Embed extensions for flutter_quill including image, video, formula and etc. -version: 0.6.1 +version: 0.6.2 homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions @@ -45,9 +45,9 @@ dependencies: # In case you are working on changes for both libraries # Comment the dependency_overrides section when publishing the package, # then uncomment it back, this will be automated later -# dependency_overrides: -# flutter_quill: -# path: ../ +dependency_overrides: + flutter_quill: + path: ../ dev_dependencies: flutter_test: diff --git a/flutter_quill_test/CHANGELOG.md b/flutter_quill_test/CHANGELOG.md index 158ce0f2..ac01b9bc 100644 --- a/flutter_quill_test/CHANGELOG.md +++ b/flutter_quill_test/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.4 +* Update `README.md` +* Documentation comments. + ## 0.0.3 * Update the `README.md` and description diff --git a/flutter_quill_test/LICENSE b/flutter_quill_test/LICENSE index 19efa532..e82b91ed 100644 --- a/flutter_quill_test/LICENSE +++ b/flutter_quill_test/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) Flutter Quill Team +Copyright (c) 2023 Flutter Quill Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/flutter_quill_test/README.md b/flutter_quill_test/README.md index d244e54b..ea602306 100644 --- a/flutter_quill_test/README.md +++ b/flutter_quill_test/README.md @@ -2,6 +2,23 @@ Test utilities for [flutter_quill](https://pub.dev/packages/flutter_quill) which includes methods to simplify interacting with the editor in test cases. +## Installation + +Run the command in your project root folder: +``` +dart pub add dev:flutter_quill_test +``` + +Example of how it will looks like: + +```yaml +dev_dependencies: + flutter_quill_test: any # Use latest Version + flutter_lints: any + flutter_test: + sdk: flutter +``` + ## Testing To aid in testing applications using the editor an extension to the flutter `WidgetTester` is provided which includes methods to simplify interacting with the editor in test cases. diff --git a/flutter_quill_test/analysis_options.yaml b/flutter_quill_test/analysis_options.yaml index 8f1b96e4..f1a38172 100644 --- a/flutter_quill_test/analysis_options.yaml +++ b/flutter_quill_test/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:lints/recommended.yaml +include: package:flutter_lints/flutter.yaml analyzer: errors: @@ -6,32 +6,31 @@ analyzer: unsafe_html: ignore linter: rules: - - always_declare_return_types - - always_put_required_named_parameters_first - - annotate_overrides - - avoid_empty_else - - avoid_escaping_inner_quotes - - avoid_print - - avoid_redundant_argument_values - - avoid_types_on_closure_parameters - - avoid_void_async - - cascade_invocations - - directives_ordering - - lines_longer_than_80_chars - - omit_local_variable_types - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_final_fields - - prefer_final_in_for_each - - prefer_final_locals - - prefer_initializing_formals - - prefer_int_literals - - prefer_interpolation_to_compose_strings - - prefer_relative_imports - - prefer_single_quotes - - sort_constructors_first - - sort_unnamed_constructors_first - - unnecessary_lambdas - - unnecessary_parenthesis - - unnecessary_string_interpolations + always_declare_return_types: true + always_put_required_named_parameters_first: true + annotate_overrides: true + avoid_empty_else: true + avoid_escaping_inner_quotes: true + avoid_print: true + avoid_redundant_argument_values: true + avoid_types_on_closure_parameters: true + avoid_void_async: true + cascade_invocations: true + directives_ordering: true + omit_local_variable_types: true + prefer_const_constructors: true + prefer_const_constructors_in_immutables: true + prefer_const_declarations: true + prefer_final_fields: true + prefer_final_in_for_each: true + prefer_final_locals: true + prefer_initializing_formals: true + prefer_int_literals: true + prefer_interpolation_to_compose_strings: true + prefer_relative_imports: true + prefer_single_quotes: true + sort_constructors_first: true + sort_unnamed_constructors_first: true + unnecessary_lambdas: true + unnecessary_parenthesis: true + unnecessary_string_interpolations: true diff --git a/flutter_quill_test/lib/src/test/widget_tester_extension.dart b/flutter_quill_test/lib/src/test/widget_tester_extension.dart index a3ef9f53..11e5f45b 100644 --- a/flutter_quill_test/lib/src/test/widget_tester_extension.dart +++ b/flutter_quill_test/lib/src/test/widget_tester_extension.dart @@ -2,21 +2,29 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_test/flutter_test.dart'; -/// Extends -extension QuillEnterText on WidgetTester { +/// Extensions on [WidgetTester] that have utilities that help +/// simplify interacting with the editor in test cases. +extension QuillWidgetTesterExt on WidgetTester { /// Give the QuillEditor widget specified by [finder] the focus. + /// Future quillGiveFocus(Finder finder) { return TestAsyncUtils.guard(() async { final editor = state( find.descendant( of: finder, - matching: find.byType(QuillEditor, skipOffstage: finder.skipOffstage), + matching: find.byType( + QuillEditor, + skipOffstage: finder.skipOffstage, + ), matchRoot: true, ), ); editor.widget.focusNode.requestFocus(); await pump(); - expect(editor.widget.focusNode.hasFocus, isTrue); + expect( + editor.widget.focusNode.hasFocus, + isTrue, + ); }); } @@ -26,6 +34,7 @@ extension QuillEnterText on WidgetTester { /// /// The widget specified by [finder] must be a [QuillEditor] or have a /// [QuillEditor] descendant. For example `find.byType(QuillEditor)`. + /// Future quillEnterText(Finder finder, String text) async { return TestAsyncUtils.guard(() async { await quillGiveFocus(finder); @@ -40,19 +49,24 @@ extension QuillEnterText on WidgetTester { /// The widget specified by [finder] must already have focus and be a /// [QuillEditor] or have a [QuillEditor] descendant. For example /// `find.byType(QuillEditor)`. + /// Future quillUpdateEditingValue(Finder finder, String text) async { return TestAsyncUtils.guard(() async { final editor = state( find.descendant( - of: finder, - matching: - find.byType(QuillRawEditor, skipOffstage: finder.skipOffstage), - matchRoot: true), + of: finder, + matching: + find.byType(QuillRawEditor, skipOffstage: finder.skipOffstage), + matchRoot: true, + ), ); - testTextInput.updateEditingValue(TextEditingValue( + testTextInput.updateEditingValue( + TextEditingValue( text: text, selection: TextSelection.collapsed( - offset: editor.textEditingValue.text.length))); + offset: editor.textEditingValue.text.length), + ), + ); await idle(); }); } diff --git a/flutter_quill_test/pubspec.yaml b/flutter_quill_test/pubspec.yaml index c8a535ff..e076d8df 100644 --- a/flutter_quill_test/pubspec.yaml +++ b/flutter_quill_test/pubspec.yaml @@ -26,12 +26,12 @@ environment: dependencies: flutter: sdk: flutter - flutter_quill: ^8.2.2 + flutter_quill: ^8.2.3 flutter_test: sdk: flutter dev_dependencies: - flutter_lints: ^2.0.0 + flutter_lints: ^3.0.1 # In case you are working on changes for both libraries # Comment the dependency_overrides section when publishing the package, diff --git a/flutter_quill_test/test/flutter_quill_test_test.dart b/flutter_quill_test/test/flutter_quill_test_test.dart index ab73b3a2..953946ba 100644 --- a/flutter_quill_test/test/flutter_quill_test_test.dart +++ b/flutter_quill_test/test/flutter_quill_test_test.dart @@ -1 +1,2 @@ +/// This will be empty for now void main() {} diff --git a/lib/src/core/utils/logger.dart b/lib/src/core/utils/logger.dart new file mode 100644 index 00000000..a0c5c805 --- /dev/null +++ b/lib/src/core/utils/logger.dart @@ -0,0 +1,68 @@ +import 'dart:async' show Zone; +import 'dart:developer' as dev show log; + +import 'package:flutter/foundation.dart' show kDebugMode; +import 'package:meta/meta.dart' show immutable; + +/// Simple logger for the quill libraries +/// +/// it log only if [kDebugMode] is true +/// so only for development mode and not in production +/// +@immutable +class QuillLogger { + const QuillLogger._(); + + static bool shouldLog() { + return kDebugMode; + } + + static void log( + T message, { + DateTime? time, + int? sequenceNumber, + int level = 0, + String name = '', + Zone? zone, + StackTrace? stackTrace, + }) { + if (!shouldLog()) { + return; + } + dev.log( + message.toString(), + time: time, + sequenceNumber: sequenceNumber, + level: level, + name: name, + zone: zone, + stackTrace: stackTrace, + ); + } + + static void error( + T message, { + DateTime? time, + int? sequenceNumber, + int level = 0, + String name = '', + Zone? zone, + Object? error, + StackTrace? stackTrace, + }) { + if (!shouldLog()) { + return; + } + + dev.log( + message.toString(), + time: time, + sequenceNumber: sequenceNumber, + level: level, + name: name, + zone: zone, + error: error, + stackTrace: stackTrace, + ); + } +} diff --git a/lib/src/widgets/proxy.dart b/lib/src/widgets/proxy.dart index 06f44191..e6475fdd 100644 --- a/lib/src/widgets/proxy.dart +++ b/lib/src/widgets/proxy.dart @@ -78,7 +78,7 @@ class RenderBaselineProxy extends RenderProxyBox { } class EmbedProxy extends SingleChildRenderObjectWidget { - const EmbedProxy(Widget child) : super(child: child); + const EmbedProxy(Widget child, {super.key}) : super(child: child); @override RenderEmbedProxy createRenderObject(BuildContext context) => diff --git a/lib/src/widgets/raw_editor/raw_editor.dart b/lib/src/widgets/raw_editor/raw_editor.dart index 8da91981..78419349 100644 --- a/lib/src/widgets/raw_editor/raw_editor.dart +++ b/lib/src/widgets/raw_editor/raw_editor.dart @@ -1164,9 +1164,10 @@ class QuillRawEditorState extends EditorState } Future _requestAutoFocusIfShould() async { + final focusManager = FocusScope.of(context); if (!_didAutoFocus && widget.autoFocus) { await Future.delayed(Duration.zero); // To avoid exceptions - FocusScope.of(context).autofocus(widget.focusNode); + focusManager.autofocus(widget.focusNode); _didAutoFocus = true; } } diff --git a/lib/src/widgets/style_widgets/checkbox_point.dart b/lib/src/widgets/style_widgets/checkbox_point.dart index 509db66f..cf57ea92 100644 --- a/lib/src/widgets/style_widgets/checkbox_point.dart +++ b/lib/src/widgets/style_widgets/checkbox_point.dart @@ -79,11 +79,11 @@ class QuillEditorCheckboxPointState extends State { if (context.requireQuillSharedConfigurations.animationConfigurations .checkBoxPointItem) { return Animate( - effects: [ - const SlideEffect( + effects: const [ + SlideEffect( duration: Duration(milliseconds: 70), ), - const ScaleEffect( + ScaleEffect( duration: Duration(milliseconds: 70), ) ], diff --git a/lib/src/widgets/text_line.dart b/lib/src/widgets/text_line.dart index b482ed79..de0c24bb 100644 --- a/lib/src/widgets/text_line.dart +++ b/lib/src/widgets/text_line.dart @@ -508,19 +508,19 @@ class _TextLineState extends State { class EditableTextLine extends RenderObjectWidget { const EditableTextLine( - this.line, - this.leading, - this.body, - this.indentWidth, - this.verticalSpacing, - this.textDirection, - this.textSelection, - this.color, - this.enableInteractiveSelection, - this.hasFocus, - this.devicePixelRatio, - this.cursorCont, - ); + this.line, + this.leading, + this.body, + this.indentWidth, + this.verticalSpacing, + this.textDirection, + this.textSelection, + this.color, + this.enableInteractiveSelection, + this.hasFocus, + this.devicePixelRatio, + this.cursorCont, + {super.key}); final Line line; final Widget? leading; diff --git a/lib/src/widgets/toolbar/base_toolbar.dart b/lib/src/widgets/toolbar/base_toolbar.dart index b422c713..573f37e1 100644 --- a/lib/src/widgets/toolbar/base_toolbar.dart +++ b/lib/src/widgets/toolbar/base_toolbar.dart @@ -102,12 +102,12 @@ class QuillToolbarDivider extends StatelessWidget { }); /// Provides a horizontal divider for vertical toolbar. - const QuillToolbarDivider.horizontal({Color? color, double? space}) - : this(Axis.horizontal, color: color, space: space); + const QuillToolbarDivider.horizontal({Key? key, Color? color, double? space}) + : this(Axis.horizontal, color: color, space: space, key: key); /// Provides a horizontal divider for horizontal toolbar. - const QuillToolbarDivider.vertical({Color? color, double? space}) - : this(Axis.vertical, color: color, space: space); + const QuillToolbarDivider.vertical({Key? key, Color? color, double? space}) + : this(Axis.vertical, color: color, space: space, key: key); /// The axis along which the toolbar is. final Axis axis; diff --git a/lib/src/widgets/toolbar/buttons/link_style.dart b/lib/src/widgets/toolbar/buttons/link_style.dart index 5234dbaf..a3c6a198 100644 --- a/lib/src/widgets/toolbar/buttons/link_style.dart +++ b/lib/src/widgets/toolbar/buttons/link_style.dart @@ -281,7 +281,7 @@ class _LinkDialogState extends State<_LinkDialog> { onChanged: _textChanged, controller: _textController, textInputAction: TextInputAction.next, - autofillHints: [ + autofillHints: const [ AutofillHints.name, AutofillHints.url, ], @@ -299,7 +299,7 @@ class _LinkDialogState extends State<_LinkDialog> { onChanged: _linkChanged, controller: _linkController, textInputAction: TextInputAction.done, - autofillHints: [AutofillHints.url], + autofillHints: const [AutofillHints.url], autocorrect: false, onEditingComplete: () { if (!_canPress()) { diff --git a/lib/src/widgets/utils/provider.dart b/lib/src/widgets/utils/provider.dart index 69340b36..6a9028e0 100644 --- a/lib/src/widgets/utils/provider.dart +++ b/lib/src/widgets/utils/provider.dart @@ -9,6 +9,7 @@ class QuillProvider extends InheritedWidget { const QuillProvider({ required this.configurations, required super.child, + super.key, }); /// Controller object which establishes a link between a rich text document @@ -67,6 +68,7 @@ class QuillToolbarProvider extends InheritedWidget { const QuillToolbarProvider({ required super.child, required this.toolbarConfigurations, + super.key, }); /// The configurations for the toolbar widget of flutter quill @@ -124,6 +126,7 @@ class QuillBaseToolbarProvider extends InheritedWidget { const QuillBaseToolbarProvider({ required super.child, required this.toolbarConfigurations, + super.key, }); /// The configurations for the toolbar widget of flutter quill @@ -181,6 +184,7 @@ class QuillEditorProvider extends InheritedWidget { const QuillEditorProvider({ required super.child, required this.editorConfigurations, + super.key, }); /// The configurations for the quill editor widget of flutter quill diff --git a/pubspec.yaml b/pubspec.yaml index 63bf0a6c..22ba0056 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill description: A rich text editor built for the modern Android, iOS, web and desktop platforms. It is the WYSIWYG editor and a Quill component for Flutter. -version: 8.2.3 +version: 8.2.4 homepage: https://1o24bbs.com/c/bulletjournal/108 repository: https://github.com/singerdmx/flutter-quill @@ -54,7 +54,7 @@ dependencies: flutter: uses-material-design: true dev_dependencies: - lints: ^3.0.0 + flutter_lints: ^3.0.1 flutter_test: sdk: flutter flutter_quill_test: ^0.0.2