From 4e5604c29e675a923c3369a3e7144cdba57c16cb Mon Sep 17 00:00:00 2001 From: zmtzawqlp Date: Thu, 25 Jun 2020 15:56:23 +0800 Subject: [PATCH] ## [2.0.0] * Breaking change: extended_text_library has changed to support OverFlowWidget [ExtendedText] * Fix textSelectionControls is not working. --- CHANGELOG.md | 17 +- example/.flutter-plugins-dependencies | 2 +- example/lib/example_route.dart | 14 -- example/lib/main.dart | 6 - example/lib/pages/custom_toolbar.dart | 4 +- example/lib/pages/main_page.dart | 23 +-- example/lib/pages/text_demo.dart | 112 +---------- example/lib/special_text/at_text.dart | 62 ++++++ example/lib/special_text/dollar_text.dart | 43 +++++ example/lib/special_text/emoji_text.dart | 36 ++-- example/lib/special_text/image_text.dart | 42 +---- .../my_extended_text_selection_controls.dart | 177 ++++++++++++++++++ .../my_special_text_span_builder.dart | 48 +++-- example/pubspec.lock | 126 ++----------- example/pubspec.yaml | 9 +- lib/src/extended_render_editable.dart | 7 +- lib/src/extended_text_field.dart | 4 +- pubspec.lock | 2 +- pubspec.yaml | 4 +- 19 files changed, 387 insertions(+), 351 deletions(-) create mode 100644 example/lib/special_text/at_text.dart create mode 100644 example/lib/special_text/dollar_text.dart create mode 100644 example/lib/special_text/my_extended_text_selection_controls.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index fd10a7a..78714d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,21 @@ +## [2.0.0] + +* Breaking change: extended_text_library has changed to support OverFlowWidget [ExtendedText] +* Fix textSelectionControls is not working. + ## [1.0.1] * Fix wrong calculation about selection handles. - + ## [1.0.0] * Merge code from 1.17.0 * Fix analysis_options - + ## [0.5.0] * Fix error caret offset on ios. - + ## [0.4.9] * Fix error about TargetPlatform.macOS @@ -26,10 +31,10 @@ * Set limitation of flutter sdk >=1.12.13 <1.12.16 * Add demo that how to delete text with code * Fix issue that not showing text while entering text from keyboard - + ## [0.4.6] -* Remove TargetPlatform.macOS +* Remove TargetPlatform.macOS ## [0.4.5] @@ -38,7 +43,7 @@ ## [0.4.4] * Fix wrong caret hegiht and postion -* Make Ios/Android caret the same height +* Make Ios/Android caret the same height ## [0.4.3] diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index a9c95c4..813c89b 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\path_provider-1.6.9\\\\","dependencies":[]},{"name":"photo_manager","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\photo_manager-0.5.2\\\\","dependencies":[]},{"name":"url_launcher","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher-5.3.0\\\\","dependencies":[]}],"android":[{"name":"path_provider","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\path_provider-1.6.9\\\\","dependencies":[]},{"name":"photo_manager","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\photo_manager-0.5.2\\\\","dependencies":[]},{"name":"url_launcher","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher-5.3.0\\\\","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\path_provider_macos-0.0.4+3\\\\","dependencies":[]},{"name":"photo_manager","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\photo_manager-0.5.2\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"url_launcher_web","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher_web-0.1.1+6\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"photo_manager","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_web"]},{"name":"url_launcher_web","dependencies":[]}],"date_created":"2020-05-31 23:04:27.859128","version":"1.17.0"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"url_launcher","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher-5.3.0\\\\","dependencies":[]}],"android":[{"name":"url_launcher","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher-5.3.0\\\\","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"url_launcher_web","path":"C:\\\\Users\\\\zmtza\\\\AppData\\\\Roaming\\\\Pub\\\\Cache\\\\hosted\\\\pub.flutter-io.cn\\\\url_launcher_web-0.1.1+6\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"url_launcher","dependencies":["url_launcher_web"]},{"name":"url_launcher_web","dependencies":[]}],"date_created":"2020-06-25 15:31:49.830705","version":"1.17.0"} \ No newline at end of file diff --git a/example/lib/example_route.dart b/example/lib/example_route.dart index afefa2c..56dd123 100644 --- a/example/lib/example_route.dart +++ b/example/lib/example_route.dart @@ -4,8 +4,6 @@ // ************************************************************************** import 'package:flutter/widgets.dart'; - -import 'package:flutter_candies_demo_library/flutter_candies_demo_library_route.dart'; import 'pages/custom_toolbar.dart'; import 'pages/main_page.dart'; import 'pages/text_demo.dart'; @@ -41,18 +39,6 @@ RouteResult getRouteResult({String name, Map arguments}) { widget: MainPage(), routeName: 'MainPage', ); - case 'fluttercandies://picswiper': - return RouteResult( - name: name, - widget: PicSwiper( - index: arguments['index'], - pics: arguments['pics'], - tuChongItem: arguments['tuChongItem'], - ), - showStatusBar: false, - routeName: 'PicSwiper', - pageRouteType: PageRouteType.transparent, - ); default: return const RouteResult(name: 'flutterCandies://notfound'); } diff --git a/example/lib/main.dart b/example/lib/main.dart index 0602eb4..70a154c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,8 +1,6 @@ -import 'package:extended_image/extended_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_candies_demo_library/flutter_candies_demo_library.dart'; import 'package:oktoast/oktoast.dart'; import 'example_route.dart'; @@ -14,9 +12,6 @@ void main() { } class MyApp extends StatelessWidget { - MyApp() { - clearDiskCachedImages(duration: const Duration(days: 7)); - } // This widget is the root of your application. @override Widget build(BuildContext context) { @@ -29,7 +24,6 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, ), builder: (BuildContext c, Widget w) { - ScreenUtil.init(width: 750, height: 1334, allowFontScaling: true); // ScreenUtil.instance = // ScreenUtil(width: 750, height: 1334, allowFontScaling: true) // ..init(c); diff --git a/example/lib/pages/custom_toolbar.dart b/example/lib/pages/custom_toolbar.dart index 6833815..9d2f171 100644 --- a/example/lib/pages/custom_toolbar.dart +++ b/example/lib/pages/custom_toolbar.dart @@ -1,10 +1,10 @@ +import 'package:example/special_text/my_extended_text_selection_controls.dart'; import 'package:example/special_text/my_special_text_span_builder.dart'; import 'package:extended_text_field/extended_text_field.dart'; import 'package:flutter/material.dart'; import 'package:ff_annotation_route/ff_annotation_route.dart'; -import 'package:flutter_candies_demo_library/flutter_candies_demo_library.dart' - hide MySpecialTextSpanBuilder; + /// /// create by zmtzawqlp on 2019/7/31 diff --git a/example/lib/pages/main_page.dart b/example/lib/pages/main_page.dart index 81808c7..aeff02a 100644 --- a/example/lib/pages/main_page.dart +++ b/example/lib/pages/main_page.dart @@ -1,8 +1,6 @@ -import 'package:extended_image/extended_image.dart'; import 'package:flutter/material.dart'; import 'package:ff_annotation_route/ff_annotation_route.dart'; -import 'package:flutter_candies_demo_library/flutter_candies_demo_library.dart'; -import 'package:oktoast/oktoast.dart'; +import 'package:url_launcher/url_launcher.dart'; import '../example_route.dart'; import '../example_routes.dart' as example_routes; @@ -86,25 +84,6 @@ class MainPage extends StatelessWidget { }, itemCount: routes.length, ), - floatingActionButton: FloatingActionButton( - onPressed: () { - ///clear memory - clearMemoryImageCache(); - - ///clear local cahced - clearDiskCachedImages().then((bool done) { - showToast(done ? 'clear succeed' : 'clear failed', - position: const ToastPosition(align: Alignment.center)); - }); - }, - child: const Text( - 'clear cache', - textAlign: TextAlign.center, - style: TextStyle( - inherit: false, - ), - ), - ), ); } } diff --git a/example/lib/pages/text_demo.dart b/example/lib/pages/text_demo.dart index 7d15a76..925d83c 100644 --- a/example/lib/pages/text_demo.dart +++ b/example/lib/pages/text_demo.dart @@ -1,5 +1,8 @@ import 'dart:math'; import 'package:example/common/toggle_button.dart'; +import 'package:example/special_text/at_text.dart'; +import 'package:example/special_text/dollar_text.dart'; +import 'package:example/special_text/my_extended_text_selection_controls.dart'; import 'package:example/special_text/my_special_text_span_builder.dart'; import 'package:extended_list/extended_list.dart'; import 'package:extended_text/extended_text.dart'; @@ -7,10 +10,9 @@ import 'package:flutter/material.dart'; import 'package:extended_text_field/extended_text_field.dart'; import 'package:flutter/services.dart'; import 'package:ff_annotation_route/ff_annotation_route.dart'; -import 'package:flutter_candies_demo_library/flutter_candies_demo_library.dart' - hide MySpecialTextSpanBuilder; import 'package:loading_more_list/loading_more_list.dart'; import 'package:example/special_text/emoji_text.dart' as emoji; +import 'package:url_launcher/url_launcher.dart'; @FFRoute( name: 'fluttercandies://TextDemo', @@ -22,7 +24,6 @@ class TextDemo extends StatefulWidget { } class _TextDemoState extends State { - TuChongRepository tuChongRepository; final TextEditingController _textEditingController = TextEditingController(); final MyExtendedMaterialTextSelectionControls _myExtendedMaterialTextSelectionControls = @@ -31,16 +32,13 @@ class _TextDemoState extends State { final MySpecialTextSpanBuilder _mySpecialTextSpanBuilder = MySpecialTextSpanBuilder(); - List images = []; - final FocusNode _focusNode = FocusNode(); double _keyboardHeight = 267.0; bool get showCustomKeyBoard => - activeEmojiGird || activeAtGrid || activeDollarGrid || activeImageGrid; + activeEmojiGird || activeAtGrid || activeDollarGrid; bool activeEmojiGird = false; bool activeAtGrid = false; bool activeDollarGrid = false; - bool activeImageGrid = false; List sessions = [ '[44] @Dota2 CN dota best dota', 'yes, you are right [36].', @@ -50,17 +48,6 @@ class _TextDemoState extends State { 'error 0 [45] warning 0', ]; - @override - void initState() { - tuChongRepository = TuChongRepository(); - super.initState(); - } - - @override - void dispose() { - tuChongRepository.dispose(); - super.dispose(); - } @override Widget build(BuildContext context) { @@ -68,7 +55,7 @@ class _TextDemoState extends State { final double keyboardHeight = MediaQuery.of(context).viewInsets.bottom; if (keyboardHeight > 0) { activeEmojiGird = - activeAtGrid = activeDollarGrid = activeImageGrid = false; + activeAtGrid = activeDollarGrid = false; } _keyboardHeight = max(_keyboardHeight, keyboardHeight); @@ -107,21 +94,6 @@ class _TextDemoState extends State { } else if (value.toString().startsWith('@')) { launch('mailto:zmtzawqlp@live.com'); } - //image - else { - final TuChongItem item = images.firstWhere( - (TuChongItem x) => x.imageUrl == value.toString()); - Navigator.pushNamed(context, 'fluttercandies://picswiper', - arguments: { - 'index': images.indexOf(item), - 'pics': item.images - .map((ImageItem f) => - PicSwiperItem( - picUrl: f.imageUrl, des: f.title)) - .toList(), - 'tuChongItem': item, - }); - } }, ); List list = [ @@ -191,7 +163,7 @@ class _TextDemoState extends State { setState(() { if (active) { activeAtGrid = - activeDollarGrid = activeImageGrid = false; + activeDollarGrid = false; FocusScope.of(context).requestFocus(_focusNode); } activeEmojiGird = active; @@ -226,7 +198,7 @@ class _TextDemoState extends State { setState(() { if (active) { activeEmojiGird = - activeDollarGrid = activeImageGrid = false; + activeDollarGrid = false; FocusScope.of(context).requestFocus(_focusNode); } activeAtGrid = active; @@ -246,7 +218,7 @@ class _TextDemoState extends State { setState(() { if (active) { activeEmojiGird = - activeAtGrid = activeImageGrid = false; + activeAtGrid = false; FocusScope.of(context).requestFocus(_focusNode); } activeDollarGrid = active; @@ -255,26 +227,6 @@ class _TextDemoState extends State { update(change); }, active: activeDollarGrid), - ToggleButton( - activeWidget: Icon( - Icons.picture_in_picture, - color: Colors.orange, - ), - unActiveWidget: Icon(Icons.picture_in_picture), - activeChanged: (bool active) { - final Function change = () { - setState(() { - if (active) { - activeEmojiGird = - activeAtGrid = activeDollarGrid = false; - FocusScope.of(context).requestFocus(_focusNode); - } - activeImageGrid = active; - }); - }; - update(change); - }, - active: activeImageGrid), Container( width: 20.0, ) @@ -326,11 +278,6 @@ class _TextDemoState extends State { if (activeDollarGrid) { return buildDollarGrid(); } - if (activeImageGrid) - return ImageGrid((TuChongItem item, String text) { - images.add(item); - insertText(text); - }, tuChongRepository); return Container(); } @@ -455,44 +402,3 @@ class _TextDemoState extends State { _textEditingController.value = value; } } - -class ImageGrid extends StatefulWidget { - const ImageGrid(this.insertText, this.tuChongRepository); - final Function(TuChongItem item, String text) insertText; - final TuChongRepository tuChongRepository; - @override - _ImageGridState createState() => _ImageGridState(); -} - -class _ImageGridState extends State - with AutomaticKeepAliveClientMixin { - @override - Widget build(BuildContext context) { - super.build(context); - - return LoadingMoreList(ListConfig( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, crossAxisSpacing: 2.0, mainAxisSpacing: 2.0), - itemBuilder: (BuildContext context, TuChongItem item, int index) { - final String url = item.imageUrl; - - /// - return GestureDetector( - child: ExtendedImage.network( - url, - fit: BoxFit.scaleDown, - ), - behavior: HitTestBehavior.translucent, - onTap: () { - widget.insertText?.call(item, - ''); - }, - ); - }, - padding: const EdgeInsets.all(5.0), - sourceList: widget.tuChongRepository)); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/example/lib/special_text/at_text.dart b/example/lib/special_text/at_text.dart new file mode 100644 index 0000000..ff9dc3c --- /dev/null +++ b/example/lib/special_text/at_text.dart @@ -0,0 +1,62 @@ +import 'package:extended_text_library/extended_text_library.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +class AtText extends SpecialText { + AtText(TextStyle textStyle, SpecialTextGestureTapCallback onTap, + {this.showAtBackground = false, this.start}) + : super(flag, ' ', textStyle, onTap: onTap); + static const String flag = '@'; + final int start; + + /// whether show background for @somebody + final bool showAtBackground; + + @override + InlineSpan finishText() { + final TextStyle textStyle = + this.textStyle?.copyWith(color: Colors.blue, fontSize: 16.0); + + final String atText = toString(); + + return showAtBackground + ? BackgroundTextSpan( + background: Paint()..color = Colors.blue.withOpacity(0.15), + text: atText, + actualText: atText, + start: start, + + ///caret can move into special text + deleteAll: true, + style: textStyle, + recognizer: (TapGestureRecognizer() + ..onTap = () { + if (onTap != null) { + onTap(atText); + } + })) + : SpecialTextSpan( + text: atText, + actualText: atText, + start: start, + style: textStyle, + recognizer: (TapGestureRecognizer() + ..onTap = () { + if (onTap != null) { + onTap(atText); + } + })); + } +} + +List atList = [ + '@Nevermore ', + '@Dota2 ', + '@Biglao ', + '@艾莉亚·史塔克 ', + '@丹妮莉丝 ', + '@HandPulledNoodles ', + '@Zmtzawqlp ', + '@FaDeKongJian ', + '@CaiJingLongDaLao ', +]; diff --git a/example/lib/special_text/dollar_text.dart b/example/lib/special_text/dollar_text.dart new file mode 100644 index 0000000..8698ed5 --- /dev/null +++ b/example/lib/special_text/dollar_text.dart @@ -0,0 +1,43 @@ +import 'package:extended_text_library/extended_text_library.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +class DollarText extends SpecialText { + DollarText(TextStyle textStyle, SpecialTextGestureTapCallback onTap, + {this.start}) + : super(flag, flag, textStyle, onTap: onTap); + static const String flag = '\$'; + final int start; + @override + InlineSpan finishText() { + final String text = getContent(); + + return SpecialTextSpan( + text: text, + actualText: toString(), + start: start, + + ///caret can move into special text + deleteAll: true, + style: textStyle?.copyWith(color: Colors.orange), + recognizer: TapGestureRecognizer() + ..onTap = () { + if (onTap != null) { + onTap(toString()); + } + }); + } +} + +List dollarList = [ + '\$Dota2\$', + '\$Dota2 Ti9\$', + '\$CN dota best dota\$', + '\$Flutter\$', + '\$CN dev best dev\$', + '\$UWP\$', + '\$Nevermore\$', + '\$FlutterCandies\$', + '\$ExtendedImage\$', + '\$ExtendedText\$', +]; diff --git a/example/lib/special_text/emoji_text.dart b/example/lib/special_text/emoji_text.dart index 2c8f105..057d66d 100644 --- a/example/lib/special_text/emoji_text.dart +++ b/example/lib/special_text/emoji_text.dart @@ -1,22 +1,19 @@ -import 'package:extended_text_field/extended_text_field.dart'; -import 'package:flutter/material.dart'; +import 'package:extended_text_library/extended_text_library.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_candies_demo_library/flutter_candies_demo_library.dart' - as demo; +import 'package:flutter/material.dart'; ///emoji/image text -class EmojiText extends demo.EmojiText { - EmojiText( - TextStyle textStyle, { - int start, - }) : super( - textStyle, - start: start, - ); - +class EmojiText extends SpecialText { + EmojiText(TextStyle textStyle, {this.start}) + : super(EmojiText.flag, ']', textStyle); + static const String flag = '['; + final int start; @override InlineSpan finishText() { final String key = toString(); + + ///https://github.com/flutter/flutter/issues/42086 + /// widget span is not working on web if (EmojiUitl.instance.emojiMap.containsKey(key) && !kIsWeb) { //fontsize id define image height //size = 30.0/26.0 * fontSize @@ -24,14 +21,16 @@ class EmojiText extends demo.EmojiText { ///fontSize 26 and text height =30.0 //final double fontSize = 26.0; - - return ImageSpan(AssetImage(EmojiUitl.instance.emojiMap[key]), + return ImageSpan( + AssetImage( + EmojiUitl.instance.emojiMap[key], + ), actualText: key, imageWidth: size, imageHeight: size, start: start, fit: BoxFit.fill, - margin: const EdgeInsets.only(left: 2.0, right: 2.0)); + margin: const EdgeInsets.only(left: 2.0, top: 2.0, right: 2.0)); } return TextSpan(text: toString(), style: textStyle); @@ -52,8 +51,5 @@ class EmojiUitl { final String _emojiFilePath = 'assets'; static EmojiUitl _instance; - static EmojiUitl get instance { - _instance ??= EmojiUitl._(); - return _instance; - } + static EmojiUitl get instance => _instance ??= EmojiUitl._(); } diff --git a/example/lib/special_text/image_text.dart b/example/lib/special_text/image_text.dart index d54de33..88c049c 100644 --- a/example/lib/special_text/image_text.dart +++ b/example/lib/special_text/image_text.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:extended_image/extended_image.dart'; import 'package:extended_text_field/extended_text_field.dart'; import 'package:flutter/material.dart' hide Element; import 'package:html/dom.dart' hide Text; @@ -76,47 +75,10 @@ class ImageText extends SpecialText { onTap: () { onTap?.call(url); }, - child: ExtendedImage.network(url, + child: Image.network(url, width: width, height: height, fit: fit, - loadStateChanged: loadStateChanged))); - } - - Widget loadStateChanged(ExtendedImageState state) { - switch (state.extendedImageLoadState) { - case LoadState.loading: - return Container( - color: Colors.grey, - ); - case LoadState.completed: - return null; - case LoadState.failed: - state.imageProvider.evict(); - return GestureDetector( - child: Stack( - fit: StackFit.expand, - children: [ - Image.asset( - 'assets/failed.jpg', - fit: BoxFit.fill, - ), - const Positioned( - bottom: 0.0, - left: 0.0, - right: 0.0, - child: Text( - 'load image failed, click to reload', - textAlign: TextAlign.center, - ), - ) - ], - ), - onTap: () { - state.reLoadImage(); - }, - ); - } - return null; + ))); } } diff --git a/example/lib/special_text/my_extended_text_selection_controls.dart b/example/lib/special_text/my_extended_text_selection_controls.dart new file mode 100644 index 0000000..8c2bb5a --- /dev/null +++ b/example/lib/special_text/my_extended_text_selection_controls.dart @@ -0,0 +1,177 @@ +import 'dart:math' as math; +import 'package:extended_text_library/extended_text_library.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:url_launcher/url_launcher.dart'; + + +// Minimal padding from all edges of the selection toolbar to all edges of the +// viewport. +const double _kHandleSize = 22.0; +const double _kToolbarScreenPadding = 8.0; +const double _kToolbarHeight = 44.0; +// Padding when positioning toolbar below selection. +const double _kToolbarContentDistanceBelow = _kHandleSize - 2.0; +const double _kToolbarContentDistance = 8.0; +/// +/// create by zmtzawqlp on 2019/8/3 +/// + +class MyExtendedMaterialTextSelectionControls + extends ExtendedMaterialTextSelectionControls { + MyExtendedMaterialTextSelectionControls(); + @override + Widget buildToolbar( + BuildContext context, + Rect globalEditableRegion, + double textLineHeight, + Offset selectionMidpoint, + List endpoints, + TextSelectionDelegate delegate, + ) { + assert(debugCheckHasMediaQuery(context)); + assert(debugCheckHasMaterialLocalizations(context)); + + // The toolbar should appear below the TextField when there is not enough + // space above the TextField to show it. + final TextSelectionPoint startTextSelectionPoint = endpoints[0]; + final TextSelectionPoint endTextSelectionPoint = endpoints.length > 1 + ? endpoints[1] + : endpoints[0]; + const double closedToolbarHeightNeeded = _kToolbarScreenPadding + + _kToolbarHeight + + _kToolbarContentDistance; + final double paddingTop = MediaQuery.of(context).padding.top; + final double availableHeight = globalEditableRegion.top + + startTextSelectionPoint.point.dy + - textLineHeight + - paddingTop; + final bool fitsAbove = closedToolbarHeightNeeded <= availableHeight; + final Offset anchor = Offset( + globalEditableRegion.left + selectionMidpoint.dx, + fitsAbove + ? globalEditableRegion.top + startTextSelectionPoint.point.dy - textLineHeight - _kToolbarContentDistance + : globalEditableRegion.top + endTextSelectionPoint.point.dy + _kToolbarContentDistanceBelow, + ); + + return Stack( + children: [ + CustomSingleChildLayout( + delegate: ExtendedMaterialTextSelectionToolbarLayout( + anchor, + _kToolbarScreenPadding + paddingTop, + fitsAbove, + ), + child: _TextSelectionToolbar( + handleCut: canCut(delegate) ? () => handleCut(delegate) : null, + handleCopy: canCopy(delegate) ? () => handleCopy(delegate) : null, + handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null, + handleSelectAll: + canSelectAll(delegate) ? () => handleSelectAll(delegate) : null, + handleLike: () { + //mailto:?subject=&body=, e.g. + launch( + 'mailto:zmtzawqlp@live.com?subject=extended_text_share&body=${delegate.textEditingValue.text}'); + delegate.hideToolbar(); + //clear selecction + delegate.textEditingValue = delegate.textEditingValue.copyWith( + selection: TextSelection.collapsed( + offset: delegate.textEditingValue.selection.end)); + }, + ), + ), + ], + ); + } + + @override + Widget buildHandle( + BuildContext context, TextSelectionHandleType type, double textHeight) { + final Widget handle = SizedBox( + width: _kHandleSize, + height: _kHandleSize, + child: Image.asset( + 'assets/40.png', + ), + ); + + // [handle] is a circle, with a rectangle in the top left quadrant of that + // circle (an onion pointing to 10:30). We rotate [handle] to point + // straight up or up-right depending on the handle type. + switch (type) { + case TextSelectionHandleType.left: // points up-right + return Transform.rotate( + angle: math.pi / 4.0, + child: handle, + ); + case TextSelectionHandleType.right: // points up-left + return Transform.rotate( + angle: -math.pi / 4.0, + child: handle, + ); + case TextSelectionHandleType.collapsed: // points up + return handle; + } + assert(type != null); + return null; + } +} + +/// Manages a copy/paste text selection toolbar. +class _TextSelectionToolbar extends StatelessWidget { + const _TextSelectionToolbar({ + Key key, + this.handleCopy, + this.handleSelectAll, + this.handleCut, + this.handlePaste, + this.handleLike, + }) : super(key: key); + + final VoidCallback handleCut; + final VoidCallback handleCopy; + final VoidCallback handlePaste; + final VoidCallback handleSelectAll; + final VoidCallback handleLike; + + @override + Widget build(BuildContext context) { + final List items = []; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); + + if (handleCut != null) { + items.add(FlatButton( + child: Text(localizations.cutButtonLabel), onPressed: handleCut)); + } + if (handleCopy != null) { + items.add(FlatButton( + child: Text(localizations.copyButtonLabel), onPressed: handleCopy)); + } + if (handlePaste != null) { + items.add(FlatButton( + child: Text(localizations.pasteButtonLabel), + onPressed: handlePaste, + )); + } + if (handleSelectAll != null) { + items.add(FlatButton( + child: Text(localizations.selectAllButtonLabel), + onPressed: handleSelectAll)); + } + + if (handleLike != null) { + items.add(FlatButton(child: Icon(Icons.favorite), onPressed: handleLike)); + } + + // If there is no option available, build an empty widget. + if (items.isEmpty) { + return Container(width: 0.0, height: 0.0); + } + + return Material( + elevation: 1.0, + child: Wrap(children: items), + borderRadius: const BorderRadius.all(Radius.circular(10.0)), + ); + } +} diff --git a/example/lib/special_text/my_special_text_span_builder.dart b/example/lib/special_text/my_special_text_span_builder.dart index 6baba06..e4e9533 100644 --- a/example/lib/special_text/my_special_text_span_builder.dart +++ b/example/lib/special_text/my_special_text_span_builder.dart @@ -1,17 +1,26 @@ import 'package:example/special_text/image_text.dart'; import 'package:extended_text_library/extended_text_library.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_candies_demo_library/flutter_candies_demo_library.dart' - hide MySpecialTextSpanBuilder; -import 'package:flutter_candies_demo_library/flutter_candies_demo_library.dart' - as demo; -import 'emoji_text.dart' as emoji; +import 'at_text.dart'; +import 'dollar_text.dart'; +import 'emoji_text.dart'; -class MySpecialTextSpanBuilder extends demo.MySpecialTextSpanBuilder { - MySpecialTextSpanBuilder({ - bool showAtBackground = false, - }) : super(showAtBackground: showAtBackground); +class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder { + MySpecialTextSpanBuilder({this.showAtBackground = false}); + + /// whether show background for @somebody + final bool showAtBackground; + @override + TextSpan build(String data, + {TextStyle textStyle, SpecialTextGestureTapCallback onTap}) { + if (kIsWeb) { + return TextSpan(text: data, style: textStyle); + } + + return super.build(data, textStyle: textStyle, onTap: onTap); + } @override SpecialText createSpecialText(String flag, @@ -20,14 +29,27 @@ class MySpecialTextSpanBuilder extends demo.MySpecialTextSpanBuilder { return null; } - if (isStart(flag, EmojiText.flag)) { - return emoji.EmojiText(textStyle, + ///index is end index of start flag, so text start index should be index-(flag.length-1) + if (isStart(flag, EmojiText.flag)) { + return EmojiText(textStyle, start: index - (EmojiText.flag.length - 1)); } else if (isStart(flag, ImageText.flag)) { return ImageText(textStyle, start: index - (ImageText.flag.length - 1), onTap: onTap); } - return super.createSpecialText(flag, - textStyle: textStyle, onTap: onTap, index: index); + else if (isStart(flag, AtText.flag)) { + return AtText( + textStyle, + onTap, + start: index - (AtText.flag.length - 1), + showAtBackground: showAtBackground, + ); + } else if (isStart(flag, EmojiText.flag)) { + return EmojiText(textStyle, start: index - (EmojiText.flag.length - 1)); + } else if (isStart(flag, DollarText.flag)) { + return DollarText(textStyle, onTap, + start: index - (DollarText.flag.length - 1)); + } + return null; } } diff --git a/example/pubspec.lock b/example/pubspec.lock index 39773bf..786391e 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.0" + version: "4.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.flutter-io.cn" source: hosted - version: "0.39.8" + version: "0.39.10" archive: dependency: transitive description: @@ -92,74 +92,53 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.3.6" - extended_image: - dependency: transitive - description: - name: extended_image - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.9.0" - extended_image_library: - dependency: transitive - description: - name: extended_image_library - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.3" extended_list: dependency: "direct main" description: name: extended_list url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.5" + version: "2.0.0" extended_list_library: dependency: transitive description: name: extended_list_library url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.3" + version: "1.0.0" extended_text: dependency: "direct main" description: path: "E:\\Flutter\\FlutterCandies\\dev\\extended_text" relative: false source: path - version: "1.0.0" + version: "2.0.0" extended_text_field: dependency: "direct main" description: path: ".." relative: true source: path - version: "1.0.0" + version: "2.0.0" extended_text_library: - dependency: "direct overridden" + dependency: transitive description: path: "E:\\Flutter\\FlutterCandies\\dev\\extended_text_library" relative: false source: path - version: "0.5.4" + version: "2.0.0" ff_annotation_route: dependency: "direct dev" description: name: ff_annotation_route url: "https://pub.flutter-io.cn" source: hosted - version: "3.2.0" + version: "3.3.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_candies_demo_library: - dependency: "direct main" - description: - path: "E:\\Flutter\\FlutterCandies\\dev\\flutter_candies_demo_library" - relative: false - source: path - version: "0.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -184,27 +163,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.14.0+3" - http: - dependency: transitive - description: - name: http - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.12.1" - http_client_helper: - dependency: transitive - description: - name: http_client_helper - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - url: "https://pub.flutter-io.cn" - source: hosted - version: "3.1.4" image: dependency: transitive description: @@ -212,13 +170,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.12" - intl: - dependency: transitive - description: - name: intl - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.15.8" io: dependency: transitive description: @@ -232,21 +183,14 @@ packages: name: js url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.1+1" - like_button: - dependency: transitive - description: - name: like_button - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.0" + version: "0.6.2" loading_more_list: dependency: "direct main" description: name: loading_more_list url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.5" + version: "3.0.0" loading_more_list_library: dependency: transitive description: @@ -283,7 +227,7 @@ packages: source: hosted version: "1.1.1" oktoast: - dependency: transitive + dependency: "direct main" description: name: oktoast url: "https://pub.flutter-io.cn" @@ -303,27 +247,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.6.4" - path_provider: - dependency: transitive - description: - name: path_provider - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.6.9" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.0.4+3" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.2" pedantic: dependency: transitive description: @@ -338,20 +261,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.4.0" - photo_manager: - dependency: transitive - description: - name: photo_manager - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.5.2" - platform: - dependency: transitive - description: - name: platform - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.2.1" platform_detect: dependency: transitive description: @@ -373,13 +282,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.4.4" - pull_to_refresh_notification: - dependency: transitive - description: - name: pull_to_refresh_notification - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.3.4" quiver: dependency: transitive description: @@ -442,7 +344,7 @@ packages: source: hosted version: "1.1.6" url_launcher: - dependency: transitive + dependency: "direct main" description: name: url_launcher url: "https://pub.flutter-io.cn" @@ -482,7 +384,7 @@ packages: name: waterfall_flow url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.3" + version: "2.0.2" xml: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ab61327..b4f0eb9 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -25,13 +25,12 @@ dependencies: cupertino_icons: ^0.1.2 extended_text_field: path: ../ - extended_text: ^1.0.0 +# extended_text: ^1.0.0 extended_list: any loading_more_list: any - flutter_candies_demo_library: - git: - url: https://github.com/fluttercandies/flutter_candies_demo_library.git - + url_launcher: 5.3.0 + oktoast: ^2.1.4 + extended_text: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter diff --git a/lib/src/extended_render_editable.dart b/lib/src/extended_render_editable.dart index e0d2328..b1dfbd2 100644 --- a/lib/src/extended_render_editable.dart +++ b/lib/src/extended_render_editable.dart @@ -330,7 +330,7 @@ class ExtendedRenderEditable extends ExtendedTextSelectionRenderObject { /// final Rect visibleRegion = Offset(0.0, _visibleRegionMinY) & size; - + //getCaretOffset ready has effectiveOffset final Offset startOffset = getCaretOffset( TextPosition( @@ -353,7 +353,7 @@ class ExtendedRenderEditable extends ExtendedTextSelectionRenderObject { _selectionStartInViewport.value = visibleRegion .inflate(visibleRegionSlop) .contains(startOffset); - + //getCaretOffset ready has effectiveOffset final Offset endOffset = getCaretOffset( TextPosition(offset: selection.end, affinity: selection.affinity), @@ -2176,4 +2176,7 @@ class ExtendedRenderEditable extends ExtendedTextSelectionRenderObject { @override Offset get effectiveOffset => _effectiveOffset; + + @override + Widget get overFlowWidget => null; } diff --git a/lib/src/extended_text_field.dart b/lib/src/extended_text_field.dart index 240eaea..4345186 100644 --- a/lib/src/extended_text_field.dart +++ b/lib/src/extended_text_field.dart @@ -890,7 +890,7 @@ class _ExtendedTextFieldState extends State case TargetPlatform.iOS: case TargetPlatform.macOS: forcePressEnabled = true; - textSelectionControls = extendedCupertinoTextSelectionControls; + textSelectionControls ??= extendedCupertinoTextSelectionControls; paintCursorAboveText = true; cursorOpacityAnimates = true; cursorColor ??= CupertinoTheme.of(context).primaryColor; @@ -904,7 +904,7 @@ class _ExtendedTextFieldState extends State case TargetPlatform.linux: case TargetPlatform.windows: forcePressEnabled = false; - textSelectionControls = extendedMaterialTextSelectionControls; + textSelectionControls ??= extendedMaterialTextSelectionControls; paintCursorAboveText = false; cursorOpacityAnimates = false; cursorColor ??= themeData.cursorColor; diff --git a/pubspec.lock b/pubspec.lock index e9dc873..85b504a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -63,7 +63,7 @@ packages: path: "E:\\Flutter\\FlutterCandies\\dev\\extended_text_library" relative: false source: path - version: "0.5.4" + version: "2.0.0" flutter: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index cbea9ed..be74858 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: extended_text_field description: Extended official text field to build special text like inline image, @somebody, custom background etc quickly.It also support to build custom seleciton toolbar and handles. -version: 1.0.1 +version: 2.0.0 author: zmtzawqlp homepage: https://github.com/fluttercandies/extended_text_field @@ -11,7 +11,7 @@ environment: dependencies: flutter: sdk: flutter - extended_text_library: ^1.0.0 + extended_text_library: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter