From 2b47be92a1a88106fc4d5878e2b5f91f639fdb18 Mon Sep 17 00:00:00 2001 From: Simon Bullik Date: Tue, 16 Nov 2021 21:32:39 +0100 Subject: [PATCH] Migrate to null safety --- example/android/build.gradle | 4 +- example/lib/main.dart | 2 +- example/pubspec.yaml | 2 +- lib/mdbean.dart | 12 +-- lib/mdeditor.dart | 195 +++++++++++++++++------------------ lib/mdviewer.dart | 20 ++-- lib/utils.dart | 42 ++++---- pubspec.lock | 14 +-- pubspec.yaml | 4 +- 9 files changed, 144 insertions(+), 151 deletions(-) diff --git a/example/android/build.gradle b/example/android/build.gradle index 041ce01..c74a135 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,7 +1,7 @@ buildscript { repositories { google() - jcenter() + gradlePluginPortal() } dependencies { @@ -12,7 +12,7 @@ buildscript { allprojects { repositories { google() - jcenter() + gradlePluginPortal() } } diff --git a/example/lib/main.dart b/example/lib/main.dart index a74b520..e149659 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -36,7 +36,7 @@ class HomePage extends StatefulWidget { class HomePageState extends State { int editingPiece = -1; - MarkdownEditorController controller; + late MarkdownEditorController controller; @override Widget build(BuildContext context) { diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 77eaef4..6934429 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -7,7 +7,7 @@ homepage: publish_to: 'none' environment: - sdk: ">=2.0.0-dev.68.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: diff --git a/lib/mdbean.dart b/lib/mdbean.dart index bbcb21d..b0049d7 100644 --- a/lib/mdbean.dart +++ b/lib/mdbean.dart @@ -1,18 +1,14 @@ class MarkDownBean { - String content; - MarkdownMeta meta; + String? content; + MarkdownMeta? meta; } class MarkdownMeta { List collection; MarkdownMeta(this.collection) { - if (collection == null) { - collection = []; - } else { - collection.sort((SelectionInfo selection1, SelectionInfo selection2) => - selection1.startIndex.compareTo(selection2.startIndex)); - } + collection.sort((SelectionInfo selection1, SelectionInfo selection2) => + selection1.startIndex.compareTo(selection2.startIndex)); } SelectionInfo selectionMeta(int user) => collection[user]; diff --git a/lib/mdeditor.dart b/lib/mdeditor.dart index 97849d2..78e6081 100644 --- a/lib/mdeditor.dart +++ b/lib/mdeditor.dart @@ -4,22 +4,22 @@ import 'package:adhara_markdown/mdviewer.dart'; import 'package:adhara_markdown/utils.dart'; class MarkdownEditor extends StatefulWidget { - final String value; - final String hint; - final bool autoFocus; - final OnSavedCallback onSaved; - final List tokenConfigs; - final TextStyle textStyle; - final TextStyle highlightedTextStyle; + final String? value; + final String? hint; + final bool? autoFocus; + final OnSavedCallback? onSaved; + final List? tokenConfigs; + final TextStyle? textStyle; + final TextStyle? highlightedTextStyle; final MarkDownBean bean; - final MarkdownEditorController controller; - final InputDecoration decoration; - final FormFieldSetter onChange; - final BoxConstraints suggestionsConstraints; + final MarkdownEditorController? controller; + final InputDecoration? decoration; + final FormFieldSetter? onChange; + final BoxConstraints? suggestionsConstraints; final Offset suggestionsOffset; MarkdownEditor( - {Key key, + {Key? key, this.value, this.hint, this.autoFocus, @@ -32,7 +32,7 @@ class MarkdownEditor extends StatefulWidget { this.decoration, this.suggestionsConstraints, this.suggestionsOffset: Offset.zero, - MarkDownBean bean}) + MarkDownBean? bean}) : bean = bean ?? MarkDownBean(), super(key: key); @@ -42,11 +42,11 @@ class MarkdownEditor extends StatefulWidget { class _MarkdownEditorState extends State with WidgetsBindingObserver { - TextEditingController textEditingController; - int currentContentLength; - Match match; + late TextEditingController textEditingController; + int? currentContentLength; + Match? match; List suggestions = []; - MarkdownTokenConfig tokenConfig; + late MarkdownTokenConfig tokenConfig; TextStyle baseTextStyle = TextStyle( color: const Color(0xff273d52), @@ -57,13 +57,13 @@ class _MarkdownEditorState extends State ); GlobalKey _editorKey = GlobalKey(); - OverlayState overlayState; + OverlayState? overlayState; @override void initState() { super.initState(); textEditingController = TextEditingController(text: widget.value); - WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance!.addObserver(this); widget.controller?._state = this; //must be called after setting textEditingController currentContentLength = textEditingController.text.length; @@ -83,12 +83,12 @@ class _MarkdownEditorState extends State @override Widget build(BuildContext context) { final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); - TextStyle effectiveTextStyle = widget.textStyle; - if (widget.textStyle == null || widget.textStyle.inherit) + TextStyle? effectiveTextStyle = widget.textStyle; + if (widget.textStyle == null || widget.textStyle!.inherit) effectiveTextStyle = defaultTextStyle.style.merge(widget.textStyle); double topOffset = _decoration.labelStyle?.fontSize ?? 0.0; if (_decoration.hintStyle != null) { - topOffset += _decoration.hintStyle.fontSize - effectiveTextStyle.fontSize; + topOffset += _decoration.hintStyle!.fontSize! - effectiveTextStyle!.fontSize!; } /*if(_decoration.contentPadding!=null){ topOffset += _decoration.contentPadding.vertical/2; @@ -103,7 +103,7 @@ class _MarkdownEditorState extends State child: MarkdownViewer( content: textEditingController.text, collapsible: false, - textStyle: effectiveTextStyle, + textStyle: effectiveTextStyle!, highlightedTextStyle: widget.highlightedTextStyle, tokenConfigs: widget.tokenConfigs, /*formatTypes: [ @@ -137,91 +137,89 @@ class _MarkdownEditorState extends State suggestions = []; match = null; }); - if (textEditingController != null) { - if (textEditingController.text.length < 1) { - suggestions = []; - for (MarkdownTokenConfig _tokenConfig in widget.tokenConfigs) { - _tokenConfig.meta?.collection = []; - } - } else { - int indexNow = textEditingController.selection.baseOffset - 1; - if (indexNow < 0) return; - for (MarkdownTokenConfig _tokenConfig in widget.tokenConfigs) { - if (_tokenConfig.hintRegExp != null) { - for (Match m in _tokenConfig.hintRegExp - .allMatches(textEditingController.text)) { - if (m.start < indexNow + 1 && m.end >= indexNow + 1) { - if (_tokenConfig.suggestions != null) { - suggestions = await _tokenConfig.suggestions( - textEditingController.text - .substring(m.start, indexNow + 1)); - } else { - suggestions = []; - } - match = m; - tokenConfig = _tokenConfig; - break; + if (textEditingController.text.length < 1) { + suggestions = []; + for (MarkdownTokenConfig _tokenConfig in widget.tokenConfigs!) { + _tokenConfig.meta?.collection = []; + } + } else { + int indexNow = textEditingController.selection.baseOffset - 1; + if (indexNow < 0) return; + for (MarkdownTokenConfig _tokenConfig in widget.tokenConfigs!) { + if (_tokenConfig.hintRegExp != null) { + for (Match m in _tokenConfig.hintRegExp! + .allMatches(textEditingController.text)) { + if (m.start < indexNow + 1 && m.end >= indexNow + 1) { + if (_tokenConfig.suggestions != null) { + suggestions = await _tokenConfig.suggestions!( + textEditingController.text + .substring(m.start, indexNow + 1)); + } else { + suggestions = []; } + match = m; + tokenConfig = _tokenConfig; + break; } - if (match != null) break; } + if (match != null) break; } - // postMeta index update - int textLength = textEditingController.text.length; - if (currentContentLength != textEditingController.text.length) { - for (MarkdownTokenConfig _tokenConfig in widget.tokenConfigs) { - if (_tokenConfig.meta != null) { - _tokenConfig.meta.collection.forEach((SelectionInfo info) { - if (textLength > currentContentLength) { - if (info.startIndex <= - indexNow - (textLength - currentContentLength) && - indexNow - (textLength - currentContentLength) < - info.endIndex) { - _tokenConfig.meta.collection = _tokenConfig.meta.collection - .where((selectionInfo) => selectionInfo != info) - .toList(); - } else if (info.startIndex > - indexNow - (textLength - currentContentLength)) { - info.updateIndex( - info.startIndex + (textLength - currentContentLength)); - } - } else { - if (info.startIndex - 1 <= indexNow && - indexNow < info.endIndex) { - _tokenConfig.meta.collection = _tokenConfig.meta.collection - .where((selectionInfo) => selectionInfo != info) - .toList(); - } else if (info.startIndex - 1 > indexNow) { - info.updateIndex( - info.startIndex - (currentContentLength - textLength)); - } + } + // postMeta index update + int textLength = textEditingController.text.length; + if (currentContentLength != textEditingController.text.length) { + for (MarkdownTokenConfig _tokenConfig in widget.tokenConfigs!) { + if (_tokenConfig.meta != null) { + _tokenConfig.meta!.collection.forEach((SelectionInfo info) { + if (textLength > currentContentLength!) { + if (info.startIndex <= + indexNow - (textLength - currentContentLength!) && + indexNow - (textLength - currentContentLength!) < + info.endIndex) { + _tokenConfig.meta!.collection = _tokenConfig.meta!.collection + .where((selectionInfo) => selectionInfo != info) + .toList(); + } else if (info.startIndex > + indexNow - (textLength - currentContentLength!)) { + info.updateIndex( + info.startIndex + (textLength - currentContentLength!)); } - }); - } + } else { + if (info.startIndex - 1 <= indexNow && + indexNow < info.endIndex) { + _tokenConfig.meta!.collection = _tokenConfig.meta!.collection + .where((selectionInfo) => selectionInfo != info) + .toList(); + } else if (info.startIndex - 1 > indexNow) { + info.updateIndex( + info.startIndex - (currentContentLength! - textLength)); + } + } + }); } } } - currentContentLength = textEditingController.text.length; - setState(() {}); } + currentContentLength = textEditingController.text.length; + setState(() {}); showSuggestions(context); if (widget.onChange != null) { - widget.onChange(textEditingController.text); + widget.onChange!(textEditingController.text); } } - OverlayEntry overlaySuggestions; + OverlayEntry? overlaySuggestions; showSuggestions(BuildContext context) async { clearSuggestions(); if (suggestions.length == 0) return; - final RenderBox renderBoxRed = _editorKey.currentContext.findRenderObject(); + final RenderBox renderBoxRed = _editorKey.currentContext!.findRenderObject() as RenderBox; final editorSize = renderBoxRed.size; final editorPosition = renderBoxRed.localToGlobal(Offset.zero); double keyboardHeight = MediaQuery.of(context).viewInsets.bottom; double visibleHeight = MediaQuery.of(context).size.height - keyboardHeight; - Offset suggestionOffset = widget.suggestionsOffset ?? Offset.zero; - double top = editorPosition.dy + editorSize.height + suggestionOffset.dy; - double bottom; + Offset suggestionOffset = widget.suggestionsOffset; + double? top = editorPosition.dy + editorSize.height + suggestionOffset.dy; + double? bottom; if (top > visibleHeight / 2) { top = null; bottom = keyboardHeight + editorSize.height + suggestionOffset.dy; @@ -235,7 +233,7 @@ class _MarkdownEditorState extends State left: suggestionOffset.dx, child: _buildSuggestions(context), )); - overlayState.insert(overlaySuggestions); + overlayState!.insert(overlaySuggestions!); } clearSuggestions() { @@ -268,11 +266,11 @@ class _MarkdownEditorState extends State shrinkWrap: true, children: suggestions.map((TokenSuggestion suggestion) { return InkWell(child: Builder(builder: (BuildContext context) { - return suggestion.display; + return suggestion.display!; }), onTap: () { int indexNow = textEditingController.selection.baseOffset; - int indexAt = match.start; - String addToInput = suggestion.onInsert(); + int indexAt = match!.start; + String addToInput = suggestion.onInsert!(); int offSet = indexAt + 1; textEditingController.text = textEditingController.text.substring(0, indexAt) + @@ -284,7 +282,7 @@ class _MarkdownEditorState extends State extentOffset: textEditingController.selection.extentOffset + offSet); if (tokenConfig.meta != null) { - tokenConfig.meta.collection.add(SelectionInfo( + tokenConfig.meta!.collection.add(SelectionInfo( indexAt, indexAt + addToInput.length - 1, suggestion.data)); } setState(() {}); @@ -295,21 +293,21 @@ class _MarkdownEditorState extends State triggerSave() { if (widget.onSaved != null) { - widget.onSaved(textEditingController.text); + widget.onSaved!(textEditingController.text); } } @override void dispose() { textEditingController.dispose(); - WidgetsBinding.instance.removeObserver(this); + WidgetsBinding.instance!.removeObserver(this); clearSuggestions(); super.dispose(); } } class MarkdownEditorController { - _MarkdownEditorState __state; + late _MarkdownEditorState __state; List listeners = []; set _state(_MarkdownEditorState state) { @@ -319,15 +317,14 @@ class MarkdownEditorController { _MarkdownEditorState get _state => __state; - TextEditingController get controller => _state?.textEditingController; - String get text => _state?.textEditingController?.text; + TextEditingController get controller => _state.textEditingController; + String get text => _state.textEditingController.text; triggerSave() { - _state?.triggerSave(); + _state.triggerSave(); } updateListeners() { - if (controller == null) return; for (VoidCallback listener in listeners) { controller.addListener(listener); } diff --git a/lib/mdviewer.dart b/lib/mdviewer.dart index a9bd342..5c7b5d5 100644 --- a/lib/mdviewer.dart +++ b/lib/mdviewer.dart @@ -4,17 +4,17 @@ import 'package:adhara_markdown/utils.dart'; class MarkdownViewer extends StatelessWidget { final String content; - final int loggedInUser; + final int? loggedInUser; final TextStyle textStyle; - final TextStyle highlightedTextStyle; + final TextStyle? highlightedTextStyle; final TextStyle fadedStyle; - final List formatTypes; + final List? formatTypes; final bool collapsible; - final List tokenConfigs; + final List? tokenConfigs; final int collapseLimit; MarkdownViewer( - {Key key, + {Key? key, this.content: "", this.loggedInUser, this.textStyle: const TextStyle(color: Colors.black), @@ -60,7 +60,7 @@ class MarkdownViewer extends StatelessWidget { richTextChildren.add(TextSpan( text: "Read more", style: fadedStyle.copyWith( - color: fadedStyle.color.withOpacity(0.6), + color: fadedStyle.color!.withOpacity(0.6), decoration: TextDecoration.underline, height: 1.3), recognizer: null, @@ -77,7 +77,7 @@ class MarkdownViewer extends StatelessWidget { _convertPostToTextSpans(BuildContext context, String content) { List contentSpans = [content]; for (MarkdownTokenConfig spanConfig in _textSpanConfigs) { - if (formatTypes == null || formatTypes.indexOf(spanConfig.type) != -1) { + if (formatTypes == null || formatTypes!.indexOf(spanConfig.type) != -1) { if (spanConfig.meta != null) { contentSpans = splitUserTokens(contentSpans, spanConfig); } else { @@ -103,7 +103,7 @@ class MarkdownViewer extends StatelessWidget { if (text is String) { int startIndex = 0; if (userSpanConfig.meta != null) { - userSpanConfig.meta.collection.forEach((SelectionInfo info) { + userSpanConfig.meta!.collection.forEach((SelectionInfo info) { returnTexts.add(text.substring(startIndex, info.startIndex)); returnTexts.add( MarkdownToken( @@ -125,7 +125,7 @@ class MarkdownViewer extends StatelessWidget { List returnTexts = []; for (var text in strings) { if (text is String) { - Iterable matches = spanConfig.regExp.allMatches(text); + Iterable matches = spanConfig.regExp!.allMatches(text); int startIndex = 0; for (Match m in matches) { returnTexts.add(text.substring(startIndex, m.start)); @@ -133,7 +133,7 @@ class MarkdownViewer extends StatelessWidget { returnTexts.add(MarkdownToken( config: spanConfig, text: (spanConfig.postProcess != null) - ? spanConfig.postProcess(matchedText) + ? spanConfig.postProcess!(matchedText) : matchedText, )); startIndex = m.end; diff --git a/lib/utils.dart b/lib/utils.dart index cb5edf0..2a3e8a2 100644 --- a/lib/utils.dart +++ b/lib/utils.dart @@ -8,7 +8,7 @@ typedef void AdharaRichTextSpanTapCallback(MarkdownToken richTextSpan); typedef Future> AdharaRichTextSuggestionCallback( String hint); typedef String OnSuggestionInsert(); -typedef void OnSavedCallback(String content); +typedef void OnSavedCallback(String? content); enum MarkdownTokenTypes { link, @@ -33,18 +33,18 @@ String stripFirstAndLast(String text) => text.substring(1, text.length - 1); String _baseCharacters = r'a-zA-Z0-9\/?.",!:<>' + r"'"; class MarkdownTokenConfig { - final MarkdownTokenTypes type; - final RegExp regExp; - final RegExp hintRegExp; - final TextStyle textStyle; - final StringCallbackFn postProcess; - final MarkdownMeta meta; - final AdharaRichTextSpanTapCallback onTap; - final AdharaRichTextSuggestionCallback suggestions; + final MarkdownTokenTypes? type; + final RegExp? regExp; + final RegExp? hintRegExp; + final TextStyle? textStyle; + final StringCallbackFn? postProcess; + final MarkdownMeta? meta; + final AdharaRichTextSpanTapCallback? onTap; + final AdharaRichTextSuggestionCallback? suggestions; MarkdownTokenConfig( - {@required this.type, - @required this.textStyle, + {required this.type, + required this.textStyle, this.regExp, this.hintRegExp, this.postProcess, @@ -71,13 +71,13 @@ class MarkdownTokenConfig { onTap = urlOpener; MarkdownTokenConfig.hashTag( - {this.textStyle, this.suggestions, this.postProcess, this.onTap}) + {required this.textStyle, this.suggestions, this.postProcess, this.onTap}) : type = MarkdownTokenTypes.hashTag, regExp = RegExp(r'#[' + _baseCharacters + ']+'), hintRegExp = RegExp(r'#[' + _baseCharacters + '\-]*'), meta = null; - MarkdownTokenConfig.bold({TextStyle textStyle, StringCallbackFn postProcess}) + MarkdownTokenConfig.bold({required TextStyle textStyle, StringCallbackFn? postProcess}) : type = MarkdownTokenTypes.bold, regExp = RegExp(r'\*[' + _baseCharacters + r'_~`\-\s]*\*'), hintRegExp = null, @@ -88,7 +88,7 @@ class MarkdownTokenConfig { onTap = null; MarkdownTokenConfig.italic( - {TextStyle textStyle, StringCallbackFn postProcess}) + {required TextStyle textStyle, StringCallbackFn? postProcess}) : type = MarkdownTokenTypes.italic, regExp = RegExp(r'_[' + _baseCharacters + r'~`\-\*\s]*_'), hintRegExp = null, @@ -99,7 +99,7 @@ class MarkdownTokenConfig { onTap = null; MarkdownTokenConfig.strikeThrough( - {TextStyle textStyle, StringCallbackFn postProcess}) + {required TextStyle textStyle, StringCallbackFn? postProcess}) : type = MarkdownTokenTypes.strikeThrough, regExp = RegExp(r'~[' + _baseCharacters + r'_`\-\*\s]*~'), hintRegExp = null, @@ -109,7 +109,7 @@ class MarkdownTokenConfig { suggestions = null, onTap = null; - MarkdownTokenConfig.code({TextStyle textStyle, StringCallbackFn postProcess}) + MarkdownTokenConfig.code({required TextStyle textStyle, StringCallbackFn? postProcess}) : type = MarkdownTokenTypes.code, regExp = RegExp(r'`[' + _baseCharacters + r'~_\-\*\s]*`'), hintRegExp = null, @@ -122,11 +122,11 @@ class MarkdownTokenConfig { class MarkdownToken { final MarkdownTokenConfig config; - final SelectionInfo selectionInfo; + final SelectionInfo? selectionInfo; final String text; MarkdownToken( - {@required this.text, @required this.config, this.selectionInfo}); + {required this.text, required this.config, this.selectionInfo}); TextSpan getSpan() { return TextSpan( @@ -135,7 +135,7 @@ class MarkdownToken { recognizer: (this.config.onTap != null) ? (TapGestureRecognizer() ..onTap = () { - this.config.onTap(this); + this.config.onTap!(this); }) : null, ); @@ -143,9 +143,9 @@ class MarkdownToken { } class TokenSuggestion { - Widget display; + Widget? display; dynamic data; - OnSuggestionInsert onInsert; + OnSuggestionInsert? onInsert; TokenSuggestion({this.display, this.data, this.onInsert}); } diff --git a/pubspec.lock b/pubspec.lock index d5d22ad..026810a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.8.1" boolean_selector: dependency: transitive description: @@ -28,7 +28,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -85,7 +85,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" path: dependency: transitive description: @@ -111,7 +111,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -146,7 +146,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.4.2" typed_data: dependency: transitive description: @@ -160,7 +160,7 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.3" + version: "6.0.4" url_launcher_linux: dependency: transitive description: @@ -204,5 +204,5 @@ packages: source: hosted version: "2.1.0" sdks: - dart: ">=2.12.0-259.9.beta <3.0.0" + dart: ">=2.12.0 <3.0.0" flutter: ">=1.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index fc2c09d..0da1923 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,12 +4,12 @@ version: 0.2.0 homepage: https://github.com/infitio/flutter_markdown environment: - sdk: ">=2.2.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: sdk: flutter - url_launcher: ^6.0.2 + url_launcher: ^6.0.4 dev_dependencies: flutter_test: