From 6193e66e1c7b1f312d4a0af14f4059462b5a985a Mon Sep 17 00:00:00 2001 From: Alexander Petermann Date: Tue, 11 Mar 2025 01:09:47 +0100 Subject: [PATCH 1/4] feature: add localization and tool tips to expandable buttons additionally, migrate to newer flutter versions --- README.md | 24 +- example/lib/main.dart | 16 +- example/pubspec.lock | 313 ++++++++++++++++-- example/pubspec.yaml | 7 +- l10n.yaml | 8 + .../markdown_editor_plus_localizations.dart | 297 +++++++++++++++++ ...markdown_editor_plus_localizations_de.dart | 92 +++++ ...markdown_editor_plus_localizations_en.dart | 92 +++++ lib/l10n/markdown_editor_plus_de.arb | 31 ++ lib/l10n/markdown_editor_plus_en.arb | 31 ++ lib/markdown_editor_plus.dart | 4 +- lib/src/markdown_syntax.dart | 2 +- lib/widgets/markdown_field.dart | 9 +- lib/widgets/markdown_toolbar.dart | 66 ++-- lib/widgets/modal_input_url.dart | 20 +- lib/widgets/toolbar_item.dart | 29 +- pubspec.lock | 79 +++-- pubspec.yaml | 14 +- 18 files changed, 995 insertions(+), 139 deletions(-) create mode 100644 l10n.yaml create mode 100644 lib/l10n/generated/markdown_editor_plus_localizations.dart create mode 100644 lib/l10n/generated/markdown_editor_plus_localizations_de.dart create mode 100644 lib/l10n/generated/markdown_editor_plus_localizations_en.dart create mode 100644 lib/l10n/markdown_editor_plus_de.arb create mode 100644 lib/l10n/markdown_editor_plus_en.arb diff --git a/README.md b/README.md index 292def5..65976cf 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ This is a fork of [simple_markdown_editor by zahnia88](https://github.com/zahniar88/simple_markdown_editor) with contributions from [fossfreaks](https://github.com/fossfreaks) -Simple markdown editor library For flutter. +Simple markdown editor library For flutter. For demo video, you can see it at this url [Demo](https://youtu.be/aYBeXXDoNPo) - ## Features + - ✅ Convert to Bold, Italic, Strikethrough - ✅ Convert to Code, Quote, Links - ✅ Convert to Heading (H1, H2, H3). +- ✅ Convert to order list - ✅ Convert to unorder list and checkbox list - ✅ Support multiline convert - ✅ Support auto convert emoji @@ -21,11 +22,26 @@ Add dependencies to your `pubspec.yaml` ```yaml dependencies: - markdown_editor_plus: ^latest + markdown_editor_plus: ^latest ``` Run `flutter pub get` to install. +Add the localization delegate: + +```dart +return MaterialApp( + localizationsDelegates: [ + MarkdownEditorPlusLocalizations.delegate, // <- Add this line + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + home: HomeScreen(), +); + +``` + ## How it works Import library @@ -96,4 +112,4 @@ MarkdownAutoPreview( ) ``` -___ +--- diff --git a/example/lib/main.dart b/example/lib/main.dart index 0fdc6b8..de1f202 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,24 +1,35 @@ import 'package:flutter/material.dart'; import 'package:markdown_editor_plus/markdown_editor_plus.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( debugShowCheckedModeBanner: false, + localizationsDelegates: [ + MarkdownEditorPlusLocalizations.delegate, // Add this line + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [ + Locale('en'), // English + Locale('de'), // German + ], home: HomeScreen(), ); } } class HomeScreen extends StatefulWidget { - const HomeScreen({Key? key}) : super(key: key); + const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); @@ -50,6 +61,7 @@ class _HomeScreenState extends State { ), emojiConvert: true, ), + MarkdownField(), ], ), ); diff --git a/example/pubspec.lock b/example/pubspec.lock index 2801d58..cf646a6 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,14 +1,38 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57 + url: "https://pub.dev" + source: hosted + version: "80.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e" + url: "https://pub.dev" + source: hosted + version: "7.3.0" + archive: + dependency: transitive + description: + name: archive + sha256: "0c64e928dcbefddecd234205422bcfc2b5e6d31be0b86fef0d0dd48d7b4c9742" + url: "https://pub.dev" + source: hosted + version: "4.0.4" args: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.6.0" async: dependency: transitive description: @@ -45,10 +69,50 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + color: + dependency: transitive + description: + name: color + sha256: ddcdf1b3badd7008233f5acffaf20ca9f5dc2cd0172b75f68f24526a5f5725cb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "3.1.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + dartx: + dependency: transitive + description: + name: dartx + sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" + url: "https://pub.dev" + source: hosted + version: "1.2.0" expandable: dependency: transitive description: @@ -65,27 +129,64 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_gen: + dependency: "direct main" + description: + name: flutter_gen + sha256: "4117a3ea6b26a910c715bd58abcc5a90447e70930a5b98249e94c41da9e849bb" + url: "https://pub.dev" + source: hosted + version: "5.10.0" + flutter_gen_core: + dependency: transitive + description: + name: flutter_gen_core + sha256: "3eaa2d3d8be58267ac4cd5e215ac965dd23cae0410dc073de2e82e227be32bfc" + url: "https://pub.dev" + source: hosted + version: "5.10.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "5.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_markdown: dependency: transitive description: name: flutter_markdown - sha256: "04c4722cc36ec5af38acc38ece70d22d3c2123c61305d555750a091517bbe504" + sha256: e7bbc718adc9476aa14cfddc1ef048d2e21e4e8f18311aaac723266db9f9e7b5 url: "https://pub.dev" source: hosted - version: "0.6.23" + version: "0.7.6+2" flutter_test: dependency: "direct dev" description: flutter @@ -95,26 +196,66 @@ packages: dependency: transitive description: name: font_awesome_flutter - sha256: "5fb789145cae1f4c3245c58b3f8fb287d055c26323879eab57a7bf0cfd1e45f3" + sha256: d3a89184101baec7f4600d58840a764d2ef760fe1c5a20ef9e6b0e9b24a07a3a + url: "https://pub.dev" + source: hosted + version: "10.8.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + hashcodes: + dependency: transitive + description: + name: hashcodes + sha256: "80f9410a5b3c8e110c4b7604546034749259f5d6dcca63e0d3c17c9258f1a651" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + image_size_getter: + dependency: transitive + description: + name: image_size_getter + sha256: "9a299e3af2ebbcfd1baf21456c3c884037ff524316c97d8e56035ea8fdf35653" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "10.5.0" + version: "0.19.0" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -127,25 +268,25 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "5.1.1" markdown: dependency: transitive description: name: markdown - sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd + sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1" url: "https://pub.dev" source: hosted - version: "7.1.1" + version: "7.3.0" markdown_editor_plus: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.2.13" + version: "0.2.16" matcher: dependency: transitive description: @@ -158,18 +299,34 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67" + url: "https://pub.dev" + source: hosted + version: "2.1.1" path: dependency: transitive description: @@ -178,11 +335,43 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + posix: + dependency: transitive + description: + name: posix + sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a + url: "https://pub.dev" + source: hosted + version: "6.0.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -195,10 +384,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -211,10 +400,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" term_glyph: dependency: transitive description: @@ -227,10 +416,42 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.3" + time: + dependency: transitive + description: + name: time + sha256: "370572cf5d1e58adcb3e354c47515da3f7469dac3a95b447117e728e7be6f461" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" + url: "https://pub.dev" + source: hosted + version: "1.1.16" vector_math: dependency: transitive description: @@ -243,10 +464,34 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + url: "https://pub.dev" + source: hosted + version: "14.3.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "3.1.3" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.6.1 <4.0.0" + flutter: ">=3.22.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 61a3153..9995358 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -23,19 +23,24 @@ environment: dependencies: flutter: sdk: flutter + flutter_gen: ^5.10.0 + flutter_localizations: + sdk: flutter + intl: any markdown_editor_plus: path: ../ dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^5.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: + generate: true # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 0000000..fdef82b --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,8 @@ +arb-dir: lib/l10n +output-dir: lib/l10n/generated +template-arb-file: markdown_editor_plus_en.arb +output-localization-file: markdown_editor_plus_localizations.dart +nullable-getter: false +use-escaping: true +synthetic-package: false +output-class: MarkdownEditorPlusLocalizations diff --git a/lib/l10n/generated/markdown_editor_plus_localizations.dart b/lib/l10n/generated/markdown_editor_plus_localizations.dart new file mode 100644 index 0000000..efa8d23 --- /dev/null +++ b/lib/l10n/generated/markdown_editor_plus_localizations.dart @@ -0,0 +1,297 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'markdown_editor_plus_localizations_de.dart'; +import 'markdown_editor_plus_localizations_en.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of MarkdownEditorPlusLocalizations +/// returned by `MarkdownEditorPlusLocalizations.of(context)`. +/// +/// Applications need to include `MarkdownEditorPlusLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'generated/markdown_editor_plus_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: MarkdownEditorPlusLocalizations.localizationsDelegates, +/// supportedLocales: MarkdownEditorPlusLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the MarkdownEditorPlusLocalizations.supportedLocales +/// property. +abstract class MarkdownEditorPlusLocalizations { + MarkdownEditorPlusLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static MarkdownEditorPlusLocalizations of(BuildContext context) { + return Localizations.of(context, MarkdownEditorPlusLocalizations)!; + } + + static const LocalizationsDelegate delegate = _MarkdownEditorPlusLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('de'), + Locale('en') + ]; + + /// No description provided for @toolbar_view_item. + /// + /// In en, this message translates to: + /// **'Show/Hide markdown preview'** + String get toolbar_view_item; + + /// No description provided for @toolbar_clear_action. + /// + /// In en, this message translates to: + /// **'Clear the text field'** + String get toolbar_clear_action; + + /// No description provided for @toolbar_reset_action. + /// + /// In en, this message translates to: + /// **'Reset the text field to specified format'** + String get toolbar_reset_action; + + /// No description provided for @toolbar_selection_action. + /// + /// In en, this message translates to: + /// **'Select single line'** + String get toolbar_selection_action; + + /// No description provided for @toolbar_bold_action. + /// + /// In en, this message translates to: + /// **'Make text bold'** + String get toolbar_bold_action; + + /// No description provided for @toolbar_italic_action. + /// + /// In en, this message translates to: + /// **'Make text italic'** + String get toolbar_italic_action; + + /// No description provided for @toolbar_strikethrough_action. + /// + /// In en, this message translates to: + /// **'Strikethrough'** + String get toolbar_strikethrough_action; + + /// No description provided for @toolbar_heading_action. + /// + /// In en, this message translates to: + /// **'Insert Heading'** + String get toolbar_heading_action; + + /// No description provided for @h1. + /// + /// In en, this message translates to: + /// **'Insert Heading 1'** + String get h1; + + /// No description provided for @h2. + /// + /// In en, this message translates to: + /// **'Insert Heading 2'** + String get h2; + + /// No description provided for @h3. + /// + /// In en, this message translates to: + /// **'Insert Heading 3'** + String get h3; + + /// No description provided for @h4. + /// + /// In en, this message translates to: + /// **'Insert Heading 4'** + String get h4; + + /// No description provided for @toolbar_order_list_action. + /// + /// In en, this message translates to: + /// **'Ordered list'** + String get toolbar_order_list_action; + + /// No description provided for @toolbar_unorder_list_action. + /// + /// In en, this message translates to: + /// **'Unordered list'** + String get toolbar_unorder_list_action; + + /// No description provided for @toolbar_checkbox_list_action. + /// + /// In en, this message translates to: + /// **'checkbox list'** + String get toolbar_checkbox_list_action; + + /// No description provided for @checkbox. + /// + /// In en, this message translates to: + /// **'Checked checkbox'** + String get checkbox; + + /// No description provided for @uncheckbox. + /// + /// In en, this message translates to: + /// **'Unchecked checkbox'** + String get uncheckbox; + + /// No description provided for @toolbar_emoji_action. + /// + /// In en, this message translates to: + /// **'Select emoji'** + String get toolbar_emoji_action; + + /// No description provided for @toolbar_link_action. + /// + /// In en, this message translates to: + /// **'Add hyperlink'** + String get toolbar_link_action; + + /// No description provided for @toolbar_link_action_template. + /// + /// In en, this message translates to: + /// **'[enter link description here]('** + String get toolbar_link_action_template; + + /// No description provided for @toolbar_image_action. + /// + /// In en, this message translates to: + /// **'Add image'** + String get toolbar_image_action; + + /// No description provided for @toolbar_image_action_template. + /// + /// In en, this message translates to: + /// **'![enter image description here]('** + String get toolbar_image_action_template; + + /// No description provided for @toolbar_blockquote_action. + /// + /// In en, this message translates to: + /// **'Blockquote'** + String get toolbar_blockquote_action; + + /// No description provided for @toolbar_code_action. + /// + /// In en, this message translates to: + /// **'Code syntax/font'** + String get toolbar_code_action; + + /// No description provided for @toolbar_line_action. + /// + /// In en, this message translates to: + /// **'Add line'** + String get toolbar_line_action; + + /// No description provided for @provide_url. + /// + /// In en, this message translates to: + /// **'Please provide a URL here.'** + String get provide_url; + + /// No description provided for @input_your_url. + /// + /// In en, this message translates to: + /// **'Input your url.'** + String get input_your_url; + + /// No description provided for @input_your_url_helper. + /// + /// In en, this message translates to: + /// **'example: https://example.com'** + String get input_your_url_helper; +} + +class _MarkdownEditorPlusLocalizationsDelegate extends LocalizationsDelegate { + const _MarkdownEditorPlusLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupMarkdownEditorPlusLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => ['de', 'en'].contains(locale.languageCode); + + @override + bool shouldReload(_MarkdownEditorPlusLocalizationsDelegate old) => false; +} + +MarkdownEditorPlusLocalizations lookupMarkdownEditorPlusLocalizations(Locale locale) { + + + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'de': return MarkdownEditorPlusLocalizationsDe(); + case 'en': return MarkdownEditorPlusLocalizationsEn(); + } + + throw FlutterError( + 'MarkdownEditorPlusLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.' + ); +} diff --git a/lib/l10n/generated/markdown_editor_plus_localizations_de.dart b/lib/l10n/generated/markdown_editor_plus_localizations_de.dart new file mode 100644 index 0000000..9ad4f16 --- /dev/null +++ b/lib/l10n/generated/markdown_editor_plus_localizations_de.dart @@ -0,0 +1,92 @@ +import 'markdown_editor_plus_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for German (`de`). +class MarkdownEditorPlusLocalizationsDe extends MarkdownEditorPlusLocalizations { + MarkdownEditorPlusLocalizationsDe([String locale = 'de']) : super(locale); + + @override + String get toolbar_view_item => 'Markdown Preview anzeigen/verstecken'; + + @override + String get toolbar_clear_action => 'Text Feld löschen'; + + @override + String get toolbar_reset_action => 'Text Feld auf Formatierung zurücksetzen'; + + @override + String get toolbar_selection_action => 'Zeile selektieren'; + + @override + String get toolbar_bold_action => 'Text Fett'; + + @override + String get toolbar_italic_action => 'Text Italic'; + + @override + String get toolbar_strikethrough_action => 'Durchstreichen'; + + @override + String get toolbar_heading_action => 'Überschrift einfügen'; + + @override + String get h1 => 'Überschrift 1 einfügen'; + + @override + String get h2 => 'Überschrift 2 einfügen'; + + @override + String get h3 => 'Überschrift 3 einfügen'; + + @override + String get h4 => 'Überschrift 4 einfügen'; + + @override + String get toolbar_order_list_action => 'Sortierte Liste'; + + @override + String get toolbar_unorder_list_action => 'Ungeordnete Liste'; + + @override + String get toolbar_checkbox_list_action => 'Liste mit Kontrollkästchen'; + + @override + String get checkbox => 'Aktiviertes Kontrollkästchen'; + + @override + String get uncheckbox => 'Leeres Kontrollkästchen'; + + @override + String get toolbar_emoji_action => 'Emoji einfügen'; + + @override + String get toolbar_link_action => 'Link einfügen'; + + @override + String get toolbar_link_action_template => '[Linkbeschreibung]('; + + @override + String get toolbar_image_action => 'Bild einfügen'; + + @override + String get toolbar_image_action_template => '![Bildbeschreibung]('; + + @override + String get toolbar_blockquote_action => 'Blockzitat'; + + @override + String get toolbar_code_action => 'Code-Block einfügen'; + + @override + String get toolbar_line_action => 'Trennlinie einfügen'; + + @override + String get provide_url => 'Bitte geben Sie die URL an.'; + + @override + String get input_your_url => 'URL eingeben.'; + + @override + String get input_your_url_helper => 'Beispiel: https://example.com'; +} diff --git a/lib/l10n/generated/markdown_editor_plus_localizations_en.dart b/lib/l10n/generated/markdown_editor_plus_localizations_en.dart new file mode 100644 index 0000000..acf2b5b --- /dev/null +++ b/lib/l10n/generated/markdown_editor_plus_localizations_en.dart @@ -0,0 +1,92 @@ +import 'markdown_editor_plus_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class MarkdownEditorPlusLocalizationsEn extends MarkdownEditorPlusLocalizations { + MarkdownEditorPlusLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get toolbar_view_item => 'Show/Hide markdown preview'; + + @override + String get toolbar_clear_action => 'Clear the text field'; + + @override + String get toolbar_reset_action => 'Reset the text field to specified format'; + + @override + String get toolbar_selection_action => 'Select single line'; + + @override + String get toolbar_bold_action => 'Make text bold'; + + @override + String get toolbar_italic_action => 'Make text italic'; + + @override + String get toolbar_strikethrough_action => 'Strikethrough'; + + @override + String get toolbar_heading_action => 'Insert Heading'; + + @override + String get h1 => 'Insert Heading 1'; + + @override + String get h2 => 'Insert Heading 2'; + + @override + String get h3 => 'Insert Heading 3'; + + @override + String get h4 => 'Insert Heading 4'; + + @override + String get toolbar_order_list_action => 'Ordered list'; + + @override + String get toolbar_unorder_list_action => 'Unordered list'; + + @override + String get toolbar_checkbox_list_action => 'checkbox list'; + + @override + String get checkbox => 'Checked checkbox'; + + @override + String get uncheckbox => 'Unchecked checkbox'; + + @override + String get toolbar_emoji_action => 'Select emoji'; + + @override + String get toolbar_link_action => 'Add hyperlink'; + + @override + String get toolbar_link_action_template => '[enter link description here]('; + + @override + String get toolbar_image_action => 'Add image'; + + @override + String get toolbar_image_action_template => '![enter image description here]('; + + @override + String get toolbar_blockquote_action => 'Blockquote'; + + @override + String get toolbar_code_action => 'Code syntax/font'; + + @override + String get toolbar_line_action => 'Add line'; + + @override + String get provide_url => 'Please provide a URL here.'; + + @override + String get input_your_url => 'Input your url.'; + + @override + String get input_your_url_helper => 'example: https://example.com'; +} diff --git a/lib/l10n/markdown_editor_plus_de.arb b/lib/l10n/markdown_editor_plus_de.arb new file mode 100644 index 0000000..d347feb --- /dev/null +++ b/lib/l10n/markdown_editor_plus_de.arb @@ -0,0 +1,31 @@ +{ + "@@locale": "de", + "toolbar_view_item": "Markdown Preview anzeigen/verstecken", + "toolbar_clear_action": "Text Feld löschen", + "toolbar_reset_action": "Text Feld auf Formatierung zurücksetzen", + "toolbar_selection_action": "Zeile selektieren", + "toolbar_bold_action": "Text Fett", + "toolbar_italic_action": "Text Italic", + "toolbar_strikethrough_action": "Durchstreichen", + "toolbar_heading_action": "Überschrift einfügen", + "h1": "Überschrift 1 einfügen", + "h2": "Überschrift 2 einfügen", + "h3": "Überschrift 3 einfügen", + "h4": "Überschrift 4 einfügen", + "toolbar_order_list_action": "Sortierte Liste", + "toolbar_unorder_list_action": "Ungeordnete Liste", + "toolbar_checkbox_list_action": "Liste mit Kontrollkästchen", + "checkbox": "Aktiviertes Kontrollkästchen", + "uncheckbox": "Leeres Kontrollkästchen", + "toolbar_emoji_action": "Emoji einfügen", + "toolbar_link_action": "Link einfügen", + "toolbar_link_action_template": "[Linkbeschreibung](", + "toolbar_image_action": "Bild einfügen", + "toolbar_image_action_template": "![Bildbeschreibung](", + "toolbar_blockquote_action": "Blockzitat", + "toolbar_code_action": "Code-Block einfügen", + "toolbar_line_action": "Trennlinie einfügen", + "provide_url": "Bitte geben Sie die URL an.", + "input_your_url": "URL eingeben.", + "input_your_url_helper": "Beispiel: https://example.com" +} diff --git a/lib/l10n/markdown_editor_plus_en.arb b/lib/l10n/markdown_editor_plus_en.arb new file mode 100644 index 0000000..508bfac --- /dev/null +++ b/lib/l10n/markdown_editor_plus_en.arb @@ -0,0 +1,31 @@ +{ + "@@locale": "en", + "toolbar_view_item": "Show/Hide markdown preview", + "toolbar_clear_action": "Clear the text field", + "toolbar_reset_action": "Reset the text field to specified format", + "toolbar_selection_action": "Select single line", + "toolbar_bold_action": "Make text bold", + "toolbar_italic_action": "Make text italic", + "toolbar_strikethrough_action": "Strikethrough", + "toolbar_heading_action": "Insert Heading", + "h1": "Insert Heading 1", + "h2": "Insert Heading 2", + "h3": "Insert Heading 3", + "h4": "Insert Heading 4", + "toolbar_order_list_action": "Ordered list", + "toolbar_unorder_list_action": "Unordered list", + "toolbar_checkbox_list_action": "checkbox list", + "checkbox": "Checked checkbox", + "uncheckbox": "Unchecked checkbox", + "toolbar_emoji_action": "Select emoji", + "toolbar_link_action": "Add hyperlink", + "toolbar_link_action_template": "[enter link description here](", + "toolbar_image_action": "Add image", + "toolbar_image_action_template": "![enter image description here](", + "toolbar_blockquote_action": "Blockquote", + "toolbar_code_action": "Code syntax/font", + "toolbar_line_action": "Add line", + "provide_url": "Please provide a URL here.", + "input_your_url": "Input your url.", + "input_your_url_helper": "example: https://example.com" +} diff --git a/lib/markdown_editor_plus.dart b/lib/markdown_editor_plus.dart index 719d3a6..d7e1da4 100644 --- a/lib/markdown_editor_plus.dart +++ b/lib/markdown_editor_plus.dart @@ -1,7 +1,9 @@ -library markdown_editor_plus; +library; export 'widgets/markdown_auto_preview.dart'; export 'widgets/markdown_parse.dart'; export 'widgets/markdown_toolbar.dart'; export 'widgets/markdown_field.dart'; export 'widgets/splitted_markdown_form_field.dart'; + +export 'l10n/generated/markdown_editor_plus_localizations.dart'; diff --git a/lib/src/markdown_syntax.dart b/lib/src/markdown_syntax.dart index 4c1176c..a24e77f 100644 --- a/lib/src/markdown_syntax.dart +++ b/lib/src/markdown_syntax.dart @@ -36,7 +36,7 @@ class ColoredHastagElementBuilder extends MarkdownElementBuilder { margin: const EdgeInsets.only(right: 5), decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), - color: Colors.blue.withOpacity(0.1), + color: Colors.blue.withAlpha(25), ), child: Text( element.textContent, diff --git a/lib/widgets/markdown_field.dart b/lib/widgets/markdown_field.dart index a359292..e8d90fd 100644 --- a/lib/widgets/markdown_field.dart +++ b/lib/widgets/markdown_field.dart @@ -17,8 +17,7 @@ class MarkdownField extends StatelessWidget { this.maxLines, this.minLines, this.expands = false, - this.decoration = - const InputDecoration(hintText: 'Type here...', isDense: true), + this.decoration = const InputDecoration(hintText: 'Type here...', isDense: true), this.padding = const EdgeInsets.all(8), }); @@ -140,12 +139,6 @@ class MarkdownField extends StatelessWidget { inputFormatters: [ if (emojiConvert) EmojiInputFormatter(), ], - toolbarOptions: const ToolbarOptions( - copy: true, - paste: true, - cut: true, - selectAll: true, - ), decoration: decoration, ), ); diff --git a/lib/widgets/markdown_toolbar.dart b/lib/widgets/markdown_toolbar.dart index 796a2b2..7b51d82 100644 --- a/lib/widgets/markdown_toolbar.dart +++ b/lib/widgets/markdown_toolbar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:markdown_editor_plus/markdown_editor_plus.dart'; import '../src/toolbar.dart'; import 'modal_select_emoji.dart'; import 'modal_input_url.dart'; @@ -52,7 +53,7 @@ class MarkdownToolbar extends StatelessWidget { key: const ValueKey("toolbar_view_item"), icon: FontAwesomeIcons.eye, onPressedButton: onPreviewChanged, - tooltip: 'Show/Hide markdown preview', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_view_item, ), // Clear the field @@ -63,7 +64,7 @@ class MarkdownToolbar extends StatelessWidget { controller.clear(); onActionCompleted?.call(); }, - tooltip: 'Clear the text field', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_clear_action, ), // Reset the text field @@ -76,7 +77,7 @@ class MarkdownToolbar extends StatelessWidget { onActionCompleted?.call(); } }, - tooltip: 'Reset the text field to specified format', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_reset_action, ), // select single line @@ -87,13 +88,13 @@ class MarkdownToolbar extends StatelessWidget { toolbar.selectSingleLine.call(); onActionCompleted?.call(); }, - tooltip: 'Select single line', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_selection_action, ), // bold ToolbarItem( key: const ValueKey("toolbar_bold_action"), icon: FontAwesomeIcons.bold, - tooltip: 'Make text bold', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_bold_action, onPressedButton: () { toolbar.action("**", "**"); onActionCompleted?.call(); @@ -103,7 +104,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("toolbar_italic_action"), icon: FontAwesomeIcons.italic, - tooltip: 'Make text italic', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_italic_action, onPressedButton: () { toolbar.action("_", "_"); onActionCompleted?.call(); @@ -113,7 +114,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("toolbar_strikethrough_action"), icon: FontAwesomeIcons.strikethrough, - tooltip: 'Strikethrough', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_strikethrough_action, onPressedButton: () { toolbar.action("~~", "~~"); onActionCompleted?.call(); @@ -124,13 +125,13 @@ class MarkdownToolbar extends StatelessWidget { key: const ValueKey("toolbar_heading_action"), icon: FontAwesomeIcons.heading, isExpandable: true, - tooltip: 'Insert Heading', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_heading_action, expandableBackground: expandableBackground, items: [ ToolbarItem( key: const ValueKey("h1"), icon: "H1", - tooltip: 'Insert Heading 1', + tooltip: MarkdownEditorPlusLocalizations.of(context).h1, onPressedButton: () { toolbar.action("# ", ""); onActionCompleted?.call(); @@ -139,7 +140,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("h2"), icon: "H2", - tooltip: 'Insert Heading 2', + tooltip: MarkdownEditorPlusLocalizations.of(context).h2, onPressedButton: () { toolbar.action("## ", ""); onActionCompleted?.call(); @@ -148,7 +149,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("h3"), icon: "H3", - tooltip: 'Insert Heading 3', + tooltip: MarkdownEditorPlusLocalizations.of(context).h3, onPressedButton: () { toolbar.action("### ", ""); onActionCompleted?.call(); @@ -157,7 +158,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("h4"), icon: "H4", - tooltip: 'Insert Heading 4', + tooltip: MarkdownEditorPlusLocalizations.of(context).h4, onPressedButton: () { toolbar.action("#### ", ""); onActionCompleted?.call(); @@ -166,11 +167,21 @@ class MarkdownToolbar extends StatelessWidget { // Heading 5 onwards has same font ], ), + // order list + ToolbarItem( + key: const ValueKey("toolbar_order_list_action"), + icon: FontAwesomeIcons.listOl, + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_order_list_action, + onPressedButton: () { + toolbar.action("1. ", ""); + onActionCompleted?.call(); + }, + ), // unorder list ToolbarItem( key: const ValueKey("toolbar_unorder_list_action"), icon: FontAwesomeIcons.listUl, - tooltip: 'Unordered list', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_unorder_list_action, onPressedButton: () { toolbar.action("* ", ""); onActionCompleted?.call(); @@ -182,11 +193,12 @@ class MarkdownToolbar extends StatelessWidget { icon: FontAwesomeIcons.listCheck, isExpandable: true, expandableBackground: expandableBackground, + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_checkbox_list_action, items: [ ToolbarItem( key: const ValueKey("checkbox"), icon: FontAwesomeIcons.solidSquareCheck, - tooltip: 'Checked checkbox', + tooltip: MarkdownEditorPlusLocalizations.of(context).checkbox, onPressedButton: () { toolbar.action("- [x] ", ""); onActionCompleted?.call(); @@ -195,7 +207,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("uncheckbox"), icon: FontAwesomeIcons.square, - tooltip: 'Unchecked checkbox', + tooltip: MarkdownEditorPlusLocalizations.of(context).uncheckbox, onPressedButton: () { toolbar.action("- [ ] ", ""); onActionCompleted?.call(); @@ -208,7 +220,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("toolbar_emoji_action"), icon: FontAwesomeIcons.faceSmile, - tooltip: 'Select emoji', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_emoji_action, onPressedButton: () async { await _showModalSelectEmoji(context, controller.selection); }, @@ -217,13 +229,12 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("toolbar_link_action"), icon: FontAwesomeIcons.link, - tooltip: 'Add hyperlink', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_link_action, onPressedButton: () async { if (toolbar.hasSelection) { - toolbar.action("[enter link description here](", ")"); + toolbar.action(MarkdownEditorPlusLocalizations.of(context).toolbar_link_action_template, ")"); } else { - await _showModalInputUrl(context, - "[enter link description here](", controller.selection); + await _showModalInputUrl(context, MarkdownEditorPlusLocalizations.of(context).toolbar_link_action_template, controller.selection); } onActionCompleted?.call(); @@ -233,14 +244,14 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("toolbar_image_action"), icon: FontAwesomeIcons.image, - tooltip: 'Add image', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_image_action, onPressedButton: () async { if (toolbar.hasSelection) { - toolbar.action("![enter image description here](", ")"); + toolbar.action(MarkdownEditorPlusLocalizations.of(context).toolbar_image_action_template, ")"); } else { await _showModalInputUrl( context, - "![enter image description here](", + MarkdownEditorPlusLocalizations.of(context).toolbar_image_action_template, controller.selection, ); } @@ -252,7 +263,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("toolbar_blockquote_action"), icon: FontAwesomeIcons.quoteLeft, - tooltip: 'Blockquote', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_blockquote_action, onPressedButton: () { toolbar.action("> ", ""); onActionCompleted?.call(); @@ -262,7 +273,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("toolbar_code_action"), icon: FontAwesomeIcons.code, - tooltip: 'Code syntax/font', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_code_action, onPressedButton: () { toolbar.action("`", "`"); onActionCompleted?.call(); @@ -272,7 +283,7 @@ class MarkdownToolbar extends StatelessWidget { ToolbarItem( key: const ValueKey("toolbar_line_action"), icon: FontAwesomeIcons.rulerHorizontal, - tooltip: 'Add line', + tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_line_action, onPressedButton: () { toolbar.action("\n___\n", ""); onActionCompleted?.call(); @@ -285,8 +296,7 @@ class MarkdownToolbar extends StatelessWidget { } // Show modal to select emoji - Future _showModalSelectEmoji( - BuildContext context, TextSelection selection) { + Future _showModalSelectEmoji(BuildContext context, TextSelection selection) { return showModalBottomSheet( isScrollControlled: true, shape: const RoundedRectangleBorder( diff --git a/lib/widgets/modal_input_url.dart b/lib/widgets/modal_input_url.dart index 25a63c4..1e1f7a5 100644 --- a/lib/widgets/modal_input_url.dart +++ b/lib/widgets/modal_input_url.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:markdown_editor_plus/src/toolbar.dart'; +import '../l10n/generated/markdown_editor_plus_localizations.dart'; + class ModalInputUrl extends StatelessWidget { const ModalInputUrl({ super.key, @@ -27,10 +29,10 @@ class ModalInputUrl extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - const Padding( + Padding( padding: EdgeInsets.all(8.0), child: Text( - "Please provide a URL here.", + MarkdownEditorPlusLocalizations.of(context).provide_url, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -41,9 +43,9 @@ class ModalInputUrl extends StatelessWidget { autocorrect: false, autofocus: true, cursorRadius: const Radius.circular(16), - decoration: const InputDecoration( - hintText: "Input your url.", - helperText: "example: https://example.com", + decoration: InputDecoration( + hintText: MarkdownEditorPlusLocalizations.of(context).input_your_url, + helperText: MarkdownEditorPlusLocalizations.of(context).input_your_url_helper, border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(16.0)), ), @@ -57,19 +59,19 @@ class ModalInputUrl extends StatelessWidget { if (value.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: const Text( - "Please input url", + content: Text( + MarkdownEditorPlusLocalizations.of(context).input_your_url, style: TextStyle( color: Colors.white, ), ), - backgroundColor: Colors.red.withOpacity(0.8), + backgroundColor: Colors.red.withAlpha(204), duration: const Duration(milliseconds: 700), ), ); } else { if (!value.contains(RegExp(r'https?:\/\/(www.)?([^\s]+)'))) { - value = "http://$value"; + value = "https://$value"; } toolbar.action( "$leftText$value)", diff --git a/lib/widgets/toolbar_item.dart b/lib/widgets/toolbar_item.dart index 4610801..53aaabe 100644 --- a/lib/widgets/toolbar_item.dart +++ b/lib/widgets/toolbar_item.dart @@ -27,8 +27,8 @@ class ToolbarItem extends StatelessWidget { type: MaterialType.transparency, child: IconButton( onPressed: onPressedButton, - splashColor: Colors.teal.withOpacity(0.4), - highlightColor: Colors.teal.withOpacity(0.4), + splashColor: Colors.teal.withAlpha(102), + highlightColor: Colors.teal.withAlpha(102), icon: icon is String ? Text( icon, @@ -45,17 +45,20 @@ class ToolbarItem extends StatelessWidget { child: Expandable( key: const Key("list_button"), collapsed: ExpandableButton( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: icon is String - ? Text( - icon, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w900, - ), - ) - : FaIcon(icon, size: 16), + child: Tooltip( + message: tooltip, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: icon is String + ? Text( + icon, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w900, + ), + ) + : FaIcon(icon, size: 16), + ), ), ), expanded: Container( diff --git a/pubspec.lock b/pubspec.lock index 05633ea..db205e3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.6.0" async: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" expandable: dependency: "direct main" description: @@ -74,18 +74,23 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_markdown: dependency: "direct main" description: name: flutter_markdown - sha256: "85cc6f7daeae537844c92e2d56e2aff61b00095f8f77913b529ea4be12fc45ea" + sha256: e7bbc718adc9476aa14cfddc1ef048d2e21e4e8f18311aaac723266db9f9e7b5 url: "https://pub.dev" source: hosted - version: "0.7.2+1" + version: "0.7.6+2" flutter_test: dependency: "direct dev" description: flutter @@ -95,26 +100,34 @@ packages: dependency: "direct main" description: name: font_awesome_flutter - sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f" + sha256: d3a89184101baec7f4600d58840a764d2ef760fe1c5a20ef9e6b0e9b24a07a3a + url: "https://pub.dev" + source: hosted + version: "10.8.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "10.7.0" + version: "0.19.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -127,18 +140,18 @@ packages: dependency: transitive description: name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.1.1" markdown: dependency: "direct main" description: name: markdown - sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 + sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1" url: "https://pub.dev" source: hosted - version: "7.2.2" + version: "7.3.0" matcher: dependency: transitive description: @@ -151,18 +164,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" path: dependency: transitive description: @@ -175,7 +188,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -188,10 +201,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -204,10 +217,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" term_glyph: dependency: transitive description: @@ -220,10 +233,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.3" vector_math: dependency: transitive description: @@ -236,10 +249,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.3.0" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.6.1 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index ad66847..d2865b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,14 +2,14 @@ name: markdown_editor_plus description: A TextField Widget that allow you to convert easily what's in the TextField to Markdown with custom toolbar support. -version: 0.2.15 +version: 1.0.0 homepage: https://github.com/OmkarDabade/markdown_editor_plus repository: https://github.com/OmkarDabade/markdown_editor_plus environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.10.0" + sdk: ^3.6.1 + flutter: ">=3.20.0" dependencies: flutter: @@ -18,10 +18,14 @@ dependencies: markdown: ^7.2.2 flutter_markdown: ^0.7.2+1 font_awesome_flutter: ^10.7.0 + flutter_localizations: + sdk: flutter + intl: any dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + flutter_lints: ^5.0.0 -flutter: null +flutter: + generate: true From f45ef6e5ba85ce4ec2cf03f7b2b92a4598d7325a Mon Sep 17 00:00:00 2001 From: Alexander Petermann Date: Tue, 11 Mar 2025 22:55:10 +0100 Subject: [PATCH 2/4] fix: Apply suggestions from code review Co-authored-by: gOAT <46922279+gOATiful@users.noreply.github.com> --- lib/l10n/markdown_editor_plus_de.arb | 14 +++++++------- lib/l10n/markdown_editor_plus_en.arb | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/l10n/markdown_editor_plus_de.arb b/lib/l10n/markdown_editor_plus_de.arb index d347feb..daddacd 100644 --- a/lib/l10n/markdown_editor_plus_de.arb +++ b/lib/l10n/markdown_editor_plus_de.arb @@ -1,12 +1,12 @@ { "@@locale": "de", - "toolbar_view_item": "Markdown Preview anzeigen/verstecken", - "toolbar_clear_action": "Text Feld löschen", - "toolbar_reset_action": "Text Feld auf Formatierung zurücksetzen", - "toolbar_selection_action": "Zeile selektieren", + "toolbar_view_item": "Markdown Vorschau anzeigen/verstecken", + "toolbar_clear_action": "Textfeld löschen", + "toolbar_reset_action": "Textfeld auf Formatierung zurücksetzen", + "toolbar_selection_action": "Zeile auswählen", "toolbar_bold_action": "Text Fett", - "toolbar_italic_action": "Text Italic", - "toolbar_strikethrough_action": "Durchstreichen", + "toolbar_italic_action": "Text kursiv", + "toolbar_strikethrough_action": "Text durchgestrichen", "toolbar_heading_action": "Überschrift einfügen", "h1": "Überschrift 1 einfügen", "h2": "Überschrift 2 einfügen", @@ -25,7 +25,7 @@ "toolbar_blockquote_action": "Blockzitat", "toolbar_code_action": "Code-Block einfügen", "toolbar_line_action": "Trennlinie einfügen", - "provide_url": "Bitte geben Sie die URL an.", + "provide_url": "Bitte geben Sie eine URL an.", "input_your_url": "URL eingeben.", "input_your_url_helper": "Beispiel: https://example.com" } diff --git a/lib/l10n/markdown_editor_plus_en.arb b/lib/l10n/markdown_editor_plus_en.arb index 508bfac..0fcea4a 100644 --- a/lib/l10n/markdown_editor_plus_en.arb +++ b/lib/l10n/markdown_editor_plus_en.arb @@ -14,7 +14,7 @@ "h4": "Insert Heading 4", "toolbar_order_list_action": "Ordered list", "toolbar_unorder_list_action": "Unordered list", - "toolbar_checkbox_list_action": "checkbox list", + "toolbar_checkbox_list_action": "Checkbox list", "checkbox": "Checked checkbox", "uncheckbox": "Unchecked checkbox", "toolbar_emoji_action": "Select emoji", @@ -27,5 +27,5 @@ "toolbar_line_action": "Add line", "provide_url": "Please provide a URL here.", "input_your_url": "Input your url.", - "input_your_url_helper": "example: https://example.com" + "input_your_url_helper": "Example: https://example.com" } From 7dc8bc626a501855dc4dc212d9f1a74bce50c6a5 Mon Sep 17 00:00:00 2001 From: Alexander Petermann Date: Tue, 11 Mar 2025 23:06:33 +0100 Subject: [PATCH 3/4] fix: allow to add a callback for the clear action button, because the textcontroller does not fire the onChanged event --- lib/widgets/markdown_auto_preview.dart | 1 + lib/widgets/markdown_toolbar.dart | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/widgets/markdown_auto_preview.dart b/lib/widgets/markdown_auto_preview.dart index 5b55634..cd1a570 100644 --- a/lib/widgets/markdown_auto_preview.dart +++ b/lib/widgets/markdown_auto_preview.dart @@ -315,6 +315,7 @@ class _MarkdownAutoPreviewState extends State { emojiConvert: widget.emojiConvert, toolbarBackground: widget.toolbarBackground, expandableBackground: widget.expandableBackground, + onClearAction: () => widget.onChanged?.call(""), ) ], ); diff --git a/lib/widgets/markdown_toolbar.dart b/lib/widgets/markdown_toolbar.dart index 7b51d82..75be9aa 100644 --- a/lib/widgets/markdown_toolbar.dart +++ b/lib/widgets/markdown_toolbar.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:markdown_editor_plus/markdown_editor_plus.dart'; +import '../l10n/generated/markdown_editor_plus_localizations.dart'; import '../src/toolbar.dart'; import 'modal_select_emoji.dart'; import 'modal_input_url.dart'; @@ -20,6 +20,7 @@ class MarkdownToolbar extends StatelessWidget { final bool showEmojiSelection; final VoidCallback? onActionCompleted; final String? markdownSyntax; + final VoidCallback? onClearAction; const MarkdownToolbar({ super.key, @@ -35,6 +36,7 @@ class MarkdownToolbar extends StatelessWidget { this.onActionCompleted, this.showPreviewButton = true, this.showEmojiSelection = true, + this.onClearAction, }); @override @@ -63,6 +65,7 @@ class MarkdownToolbar extends StatelessWidget { onPressedButton: () { controller.clear(); onActionCompleted?.call(); + onClearAction?.call(); }, tooltip: MarkdownEditorPlusLocalizations.of(context).toolbar_clear_action, ), From b1d44bedd1e7754f9dbb7d006256f6c8f42e0b75 Mon Sep 17 00:00:00 2001 From: Alexander Petermann Date: Wed, 12 Mar 2025 14:35:03 +0100 Subject: [PATCH 4/4] feature: allow to set initialText on MarkdownAutoPreview without providing a controller. also, copy over the documentation from MarkdownAutoPreview to MarkdownField --- lib/widgets/markdown_auto_preview.dart | 25 +++++++++++----------- lib/widgets/markdown_field.dart | 29 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/lib/widgets/markdown_auto_preview.dart b/lib/widgets/markdown_auto_preview.dart index cd1a570..abc6562 100644 --- a/lib/widgets/markdown_auto_preview.dart +++ b/lib/widgets/markdown_auto_preview.dart @@ -30,6 +30,7 @@ class MarkdownAutoPreview extends StatefulWidget { this.expands = false, this.decoration = const InputDecoration(isDense: true), this.hintText, + this.initialText, }); /// Markdown syntax to reset the field to @@ -181,6 +182,11 @@ class MarkdownAutoPreview extends StatefulWidget { /// Defaults to false. final bool expands; + /// [initialText] used to initialize the internal [controller] text. + /// + /// only one, the [initialText] OR the [controller] can be set, not both. + final String? initialText; + @override State createState() => _MarkdownAutoPreviewState(); } @@ -189,10 +195,8 @@ class _MarkdownAutoPreviewState extends State { // Internal parameter late TextEditingController _internalController; - final FocusScopeNode _internalFocus = - FocusScopeNode(debugLabel: '_internalFocus'); - final FocusNode _textFieldFocusNode = - FocusNode(debugLabel: '_textFieldFocusNode'); + final FocusScopeNode _internalFocus = FocusScopeNode(debugLabel: '_internalFocus'); + final FocusNode _textFieldFocusNode = FocusNode(debugLabel: '_textFieldFocusNode'); late Toolbar _toolbar; @@ -200,7 +204,8 @@ class _MarkdownAutoPreviewState extends State { @override void initState() { - _internalController = widget.controller ?? TextEditingController(); + assert((widget.controller != null) ^ (widget.initialText != null)); + _internalController = widget.controller ?? TextEditingController(text: widget.initialText); _toolbar = Toolbar( controller: _internalController, @@ -222,10 +227,8 @@ class _MarkdownAutoPreviewState extends State { Widget build(BuildContext context) { return FocusableActionDetector( shortcuts: { - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyB): - BoldTextIntent(), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyI): - ItalicTextIntent(), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyB): BoldTextIntent(), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyI): ItalicTextIntent(), }, actions: { BoldTextIntent: CallbackAction( @@ -273,9 +276,7 @@ class _MarkdownAutoPreviewState extends State { alignment: Alignment.centerLeft, child: MarkdownBody( key: const ValueKey("zmarkdown-parse-body"), - data: _internalController.text == "" - ? widget.hintText ?? "_Markdown text_" - : _internalController.text, + data: _internalController.text == "" ? widget.hintText ?? "_Markdown text_" : _internalController.text, ), ), ), diff --git a/lib/widgets/markdown_field.dart b/lib/widgets/markdown_field.dart index e8d90fd..fa0c047 100644 --- a/lib/widgets/markdown_field.dart +++ b/lib/widgets/markdown_field.dart @@ -112,8 +112,37 @@ class MarkdownField extends StatelessWidget { /// Add label, hint etc final InputDecoration decoration; + /// The maximum number of lines to show at one time, wrapping if necessary. + /// + /// This affects the height of the field itself and does not limit the number of lines that can be entered into the field. + /// + /// If this is 1 (the default), the text will not wrap, but will scroll horizontally instead. + /// + /// If this is null, there is no limit to the number of lines, and the text container will start with enough vertical space for one line and automatically grow to accommodate additional lines as they are entered, up to the height of its constraints. + /// + /// If this is not null, the value must be greater than zero, and it will lock the input to the given number of lines and take up enough horizontal space to accommodate that number of lines. Setting [minLines] as well allows the input to grow and shrink between the indicated range. final int? maxLines; + + /// The minimum number of lines to occupy when the content spans fewer lines. + /// + /// This affects the height of the field itself and does not limit the number of lines that can be entered into the field. + /// + /// If this is null (default), text container starts with enough vertical space for one line and grows to accommodate additional lines as they are entered. + /// + /// This can be used in combination with [maxLines] for a varying set of behaviors. + /// + /// If the value is set, it must be greater than zero. If the value is greater than 1, [maxLines] should also be set to either null or greater than this value. + /// + /// When [maxLines] is set as well, the height will grow between the indicated range of lines. When [maxLines] is null, it will grow as high as needed, starting from [minLines]. final int? minLines; + + /// Whether this widget's height will be sized to fill its parent. + /// + /// If set to true and wrapped in a parent widget like [Expanded] or [SizedBox], the input will expand to fill the parent. + /// + /// [maxLines] and [minLines] must both be null when this is set to true, otherwise an error is thrown. + /// + /// Defaults to false. final bool expands; @override