Skip to content

Commit

Permalink
Merge pull request #2 from eyeem/null_safety
Browse files Browse the repository at this point in the history
Upgrade package to Dart 2.12 and sound null safety.
Sebastian Engel authored Sep 29, 2021
2 parents 14e8618 + 9870c3f commit 4fca915
Showing 12 changed files with 90 additions and 120 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [0.2.0] - 22/09/2021

* Upgrade package to Dart 2.12 and sound null safety.

## [0.1.0] - 22/02/2020

* Bump up version, API is quite stable and we use the package successfully in production.
4 changes: 4 additions & 0 deletions packages/span_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [0.2.0] - 22/09/2021

* Upgrade package to Dart 2.12 and sound null safety.

## [0.1.0] - 22/02/2020

* Bump up version, API is quite stable and we use the package successfully in production.
4 changes: 2 additions & 2 deletions packages/span_builder/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -33,12 +33,12 @@ class MyApp extends StatelessWidget {
style: TextStyle(
decoration: TextDecoration.underline)),
onTap: () {
Scaffold.of(context).showSnackBar(
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("weeeee")));
})
..apply(const TextSpan(text: "🐶"), whereText: "dog"),
defaultStyle:
TextStyle(color: Colors.black, fontSize: 32.0),
const TextStyle(color: Colors.black, fontSize: 32.0),
textAlign: TextAlign.center)))));
}
}
112 changes: 35 additions & 77 deletions packages/span_builder/example/pubspec.lock
Original file line number Diff line number Diff line change
@@ -1,69 +1,62 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.2"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
version: "2.8.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
charcode:
version: "2.1.0"
characters:
dependency: transitive
description:
name: charcode
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
collection:
version: "1.1.0"
charcode:
dependency: transitive
description:
name: collection
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
convert:
version: "1.3.1"
clock:
dependency: transitive
description:
name: convert
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
crypto:
version: "1.1.0"
collection:
dependency: transitive
description:
name: crypto
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
version: "1.15.0"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
@@ -74,55 +67,27 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.6"
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.8"
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.4"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0+1"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "1.8.0"
sky_engine:
dependency: transitive
description: flutter
@@ -134,76 +99,69 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.5"
version: "1.8.1"
span_builder:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.0.1"
version: "0.2.0"
span_builder_test:
dependency: "direct dev"
description:
path: "../../span_builder_test"
relative: true
source: path
version: "0.0.1"
version: "0.2.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.11"
version: "0.4.2"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "3.5.0"
version: "2.1.0"
sdks:
dart: ">=2.6.0 <3.0.0"
dart: ">=2.12.0 <3.0.0"
2 changes: 1 addition & 1 deletion packages/span_builder/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ description: Example app for span_builder
version: 1.0.0+1

environment:
sdk: ">=2.1.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'

dependencies:
flutter:
4 changes: 2 additions & 2 deletions packages/span_builder/example/test/widget_test.dart
Original file line number Diff line number Diff line change
@@ -10,12 +10,12 @@ void main() {
final spanFinder = find.byKey(span_key);

expect(spanFinder, findsOneWidget);
final allSpans = tester.findSpans(spanFinder).length;
final allSpans = tester.findSpans(spanFinder)?.length;
expect(allSpans, 8);

final foxSpans = tester.findSpans(spanFinder, predicate: (span) {
return span is TextSpan && span.text == "🦊";
});
expect(foxSpans.length, 1);
expect(foxSpans?.length, 1);
});
}
54 changes: 27 additions & 27 deletions packages/span_builder/lib/src/span_builder.dart
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart';
/// represents position of a span in some plain text
class SpanEntity {
const SpanEntity(
{@required this.start, @required this.end, @required this.span})
{required this.start, required this.end, required this.span})
: assert(start >= 0),

/// we only accept _ManagedTextSpans since we must take care of managing the recognizer
@@ -26,12 +26,12 @@ class SpanEntity {
}

extension ManagedTextSpanExtension on TextSpan {
InlineSpan managed([RecognizerBuilder recognizerBuilder]) =>
InlineSpan managed([RecognizerBuilder? recognizerBuilder]) =>
_ManagedTextSpan.fromTextSpan(this, recognizerBuilder: recognizerBuilder);
}

typedef RecognizerBuilder = GestureRecognizer Function();
typedef _RecognizerBuilder = GestureRecognizer Function(_ManagedTextSpan);
typedef _RecognizerBuilder = GestureRecognizer? Function(_ManagedTextSpan);

/// Due to some [poor design choices](https://github.com/flutter/flutter/issues/10623#issuecomment-345790443)
/// you need to dispose [TextSpan]'s [GestureRecognizer] yourself. When you are formatting text, thinking about
@@ -41,20 +41,20 @@ typedef _RecognizerBuilder = GestureRecognizer Function(_ManagedTextSpan);
class _ManagedTextSpan extends TextSpan {
const _ManagedTextSpan({
this.recognizerBuilder,
String text,
TextStyle style,
String semanticsLabel,
String? text,
TextStyle? style,
String? semanticsLabel,
}) : super(text: text, style: style, semanticsLabel: semanticsLabel);
_ManagedTextSpan.fromTextSpan(TextSpan textSpan,
{RecognizerBuilder recognizerBuilder})
{RecognizerBuilder? recognizerBuilder})
: this(
text: textSpan.text,
style: textSpan.style,
semanticsLabel: textSpan.semanticsLabel,
recognizerBuilder: recognizerBuilder);
final RecognizerBuilder recognizerBuilder;
final RecognizerBuilder? recognizerBuilder;

TextSpan withManagedRecognizer(GestureRecognizer managedRecognizer) {
TextSpan withManagedRecognizer(GestureRecognizer? managedRecognizer) {
return TextSpan(
style: style,
text: text,
@@ -85,11 +85,11 @@ class SpanBuilder {
final _entities = <SpanEntity>[];

SpanBuilder apply(InlineSpan span,
{String whereText,
int from,
int to,
Function() onTap,
RecognizerBuilder recognizerBuilder}) {
{String? whereText,
int? from,
int? to,
Function()? onTap,
RecognizerBuilder? recognizerBuilder}) {
/// turn any [onTap] into [RecognizerBuilder]
if (onTap != null && recognizerBuilder == null) {
recognizerBuilder = () => TapGestureRecognizer()..onTap = onTap;
@@ -98,7 +98,7 @@ class SpanBuilder {
/// convert [TextSpan] into [_ManagedTextSpan] with a [RecoginzerBuilder]
if (span is TextSpan) {
/// ignore: avoid_as
span = (span as TextSpan).managed(recognizerBuilder);
span = span.managed(recognizerBuilder);
}

/// if [whereText] is not provided then we try to figure it out:
@@ -134,7 +134,7 @@ class SpanBuilder {
}

/// finally we appen calculated SpanPosition
return add(SpanEntity(start: offset + from, end: offset + to, span: span));
return add(SpanEntity(start: offset + from!, end: offset + to!, span: span));
}

/// we sort entities when we add them, if any of the entities is overlapping,
@@ -155,16 +155,16 @@ class SpanBuilder {
return this;
}

List<InlineSpan> build({_RecognizerBuilder recognizerBuilder}) =>
List<InlineSpan> build({_RecognizerBuilder? recognizerBuilder}) =>
_computeSpans(sourceText, _entities, recognizerBuilder);
}

/// A wrapper around [RichText] allowing you to use [SpanBuilder] efficiently. This class handles
/// disposing any related instances of [GestureRecognizer] that might be used in your text.
class SpanBuilderWidget extends StatefulWidget {
const SpanBuilderWidget(
{Key key,
@required this.text,
{Key? key,
required this.text,
this.defaultStyle,
this.textAlign = TextAlign.start,
this.textDirection,
@@ -177,7 +177,7 @@ class SpanBuilderWidget extends StatefulWidget {
this.textWidthBasis = TextWidthBasis.parent})
: super(key: key);
final SpanBuilder text;
final TextStyle defaultStyle;
final TextStyle? defaultStyle;

@override
State<StatefulWidget> createState() => _SpanBuilderWidgetState();
@@ -187,21 +187,21 @@ class SpanBuilderWidget extends StatefulWidget {

/// wrapping [RichText] fields
final TextAlign textAlign;
final TextDirection textDirection;
final TextDirection? textDirection;
final bool softWrap;
final TextOverflow overflow;
final double textScaleFactor;
final int maxLines;
final Locale locale;
final StrutStyle strutStyle;
final int? maxLines;
final Locale? locale;
final StrutStyle? strutStyle;
final TextWidthBasis textWidthBasis;
}

class _SpanBuilderWidgetState extends State<SpanBuilderWidget> {
TextSpan _textSpan;
late TextSpan _textSpan;
final _recongnizers = <GestureRecognizer>[];

GestureRecognizer recognizerBuilder(_ManagedTextSpan textSpan) {
GestureRecognizer? recognizerBuilder(_ManagedTextSpan textSpan) {
final recognizer = textSpan.recognizerBuilder?.call();
if (recognizer != null) {
if (SpanBuilderWidget.debugPrint) {
@@ -268,7 +268,7 @@ class _SpanBuilderWidgetState extends State<SpanBuilderWidget> {

/// CONTRACT: entities must come sorted by their appearance and should not overlap
List<InlineSpan> _computeSpans(String text, List<SpanEntity> entities,
_RecognizerBuilder recognizerBuilder) {
_RecognizerBuilder? recognizerBuilder) {
final output = <InlineSpan>[];
var currentIndex = 0;
for (final entity in entities) {
4 changes: 2 additions & 2 deletions packages/span_builder/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: span_builder
description: Facilitates creation of spans from plain text and provides automated disposal of GestureRecognizers
version: 0.1.0
version: 0.2.0
homepage: https://github.com/eyeem/span_builder

environment:
sdk: ">=2.6.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'

dependencies:
flutter:
6 changes: 3 additions & 3 deletions packages/span_builder/test/span_builder_test.dart
Original file line number Diff line number Diff line change
@@ -42,12 +42,12 @@ void main() {
expect(tapped, isFalse);

final textWidget = tester.widget<RichText>(find.byType(RichText));
final TextSpan span = textWidget.text;
final spans = span.children;
final span = textWidget.text as TextSpan;
final spans = span.children!;
expect(spans, hasLength(4));

// fake tap
((spans[1] as TextSpan).recognizer as TapGestureRecognizer).onTap();
((spans[1] as TextSpan).recognizer as TapGestureRecognizer).onTap!();

expect(tapped, isTrue);
});
4 changes: 4 additions & 0 deletions packages/span_builder_test/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [0.2.0] - 22/09/2021

* Upgrade package to Dart 2.12 and sound null safety.

## [0.1.0] - 22/02/2020

* Bump up version, API is quite stable and we use the package successfully in production.
8 changes: 4 additions & 4 deletions packages/span_builder_test/lib/src/span_builder_test.dart
Original file line number Diff line number Diff line change
@@ -5,15 +5,15 @@ import 'package:flutter_test/flutter_test.dart';
/// https://github.com/flutter/flutter/blob/master/packages/flutter/test/widgets/hyperlink_test.dart#L47
/// sooo...
extension WidgetTesterRichEditExtension on WidgetTester {
Iterable<InlineSpan> findSpans(Finder spanWidgetFinder,
{bool Function(InlineSpan) predicate}) {
Iterable<InlineSpan>? findSpans(Finder spanWidgetFinder,
{bool Function(InlineSpan)? predicate}) {
final richTextFinder =
find.descendant(of: spanWidgetFinder, matching: find.byType(RichText));
final richText = widget<RichText>(richTextFinder);
expect(richText.text, isInstanceOf<TextSpan>());
final TextSpan innerText = richText.text;
final innerText = richText.text as TextSpan;
return predicate == null
? innerText.children
: innerText.children.where(predicate);
: innerText.children?.where(predicate);
}
}
4 changes: 2 additions & 2 deletions packages/span_builder_test/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: span_builder_test
description: WidgetTester helpers and extensions for testing span_builder
version: 0.1.0
version: 0.2.0
homepage: https://github.com/eyeem/span_builder_test

environment:
sdk: ">=2.6.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'

dependencies:
flutter:

0 comments on commit 4fca915

Please sign in to comment.