mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 13:33:32 +00:00
Add scroll to position of notification in post screen
This commit is contained in:
parent
3dc6751281
commit
a1ac7a009f
9 changed files with 138 additions and 44 deletions
|
@ -25,12 +25,13 @@ class NotificationControl extends StatelessWidget {
|
||||||
final manager = getIt<TimelineManager>();
|
final manager = getIt<TimelineManager>();
|
||||||
final existingPostData = manager.getPostTreeEntryBy(notification.iid);
|
final existingPostData = manager.getPostTreeEntryBy(notification.iid);
|
||||||
if (existingPostData.isSuccess) {
|
if (existingPostData.isSuccess) {
|
||||||
context.push('/post/view/${existingPostData.value.id}');
|
context
|
||||||
|
.push('/post/view/${existingPostData.value.id}/${notification.iid}');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final loadedPost = await manager.refreshStatusChain(notification.iid);
|
final loadedPost = await manager.refreshStatusChain(notification.iid);
|
||||||
if (loadedPost.isSuccess) {
|
if (loadedPost.isSuccess) {
|
||||||
context.push('/post/view/${loadedPost.value.id}');
|
context.push('/post/view/${loadedPost.value.id}/${notification.iid}');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buildSnackbar(
|
buildSnackbar(
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
|
||||||
import '../../models/entry_tree_item.dart';
|
import '../../models/entry_tree_item.dart';
|
||||||
|
import '../../models/flattened_tree_item.dart';
|
||||||
import '../../models/timeline_entry.dart';
|
import '../../models/timeline_entry.dart';
|
||||||
import '../../services/timeline_manager.dart';
|
import '../../services/timeline_manager.dart';
|
||||||
import '../../utils/entry_tree_item_flattening.dart';
|
import '../../utils/entry_tree_item_flattening.dart';
|
||||||
|
@ -10,14 +12,19 @@ import 'flattened_tree_entry_control.dart';
|
||||||
|
|
||||||
class PostControl extends StatefulWidget {
|
class PostControl extends StatefulWidget {
|
||||||
final EntryTreeItem originalItem;
|
final EntryTreeItem originalItem;
|
||||||
|
final String scrollToId;
|
||||||
final bool openRemote;
|
final bool openRemote;
|
||||||
final bool showStatusOpenButton;
|
final bool showStatusOpenButton;
|
||||||
|
final bool isRoot;
|
||||||
|
|
||||||
const PostControl(
|
const PostControl({
|
||||||
{super.key,
|
super.key,
|
||||||
required this.originalItem,
|
required this.originalItem,
|
||||||
required this.openRemote,
|
required this.scrollToId,
|
||||||
required this.showStatusOpenButton});
|
required this.openRemote,
|
||||||
|
required this.showStatusOpenButton,
|
||||||
|
required this.isRoot,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PostControl> createState() => _PostControlState();
|
State<PostControl> createState() => _PostControlState();
|
||||||
|
@ -26,6 +33,10 @@ class PostControl extends StatefulWidget {
|
||||||
class _PostControlState extends State<PostControl> {
|
class _PostControlState extends State<PostControl> {
|
||||||
static final _logger = Logger('$PostControl');
|
static final _logger = Logger('$PostControl');
|
||||||
|
|
||||||
|
final ItemScrollController itemScrollController = ItemScrollController();
|
||||||
|
final ItemPositionsListener itemPositionsListener =
|
||||||
|
ItemPositionsListener.create();
|
||||||
|
|
||||||
var showContent = true;
|
var showContent = true;
|
||||||
|
|
||||||
var showComments = false;
|
var showComments = false;
|
||||||
|
@ -41,6 +52,7 @@ class _PostControlState extends State<PostControl> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
showContent = entry.spoilerText.isEmpty;
|
showContent = entry.spoilerText.isEmpty;
|
||||||
|
showComments = widget.scrollToId != item.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -48,6 +60,19 @@ class _PostControlState extends State<PostControl> {
|
||||||
final manager = context.watch<TimelineManager>();
|
final manager = context.watch<TimelineManager>();
|
||||||
_logger.finest('Building ${item.entry.toShortString()}');
|
_logger.finest('Building ${item.entry.toShortString()}');
|
||||||
final items = widget.originalItem.flatten(topLevelOnly: !showComments);
|
final items = widget.originalItem.flatten(topLevelOnly: !showComments);
|
||||||
|
|
||||||
|
if (widget.isRoot) {
|
||||||
|
return buildListView(context, items, manager);
|
||||||
|
} else {
|
||||||
|
return buildColumnView(context, items, manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildColumnView(
|
||||||
|
BuildContext context,
|
||||||
|
List<FlattenedTreeItem> items,
|
||||||
|
TimelineManager manager,
|
||||||
|
) {
|
||||||
final widgets = <Widget>[];
|
final widgets = <Widget>[];
|
||||||
for (var i = 0; i < items.length; i++) {
|
for (var i = 0; i < items.length; i++) {
|
||||||
final itemWidget = FlattenedTreeEntryControl(
|
final itemWidget = FlattenedTreeEntryControl(
|
||||||
|
@ -76,4 +101,61 @@ class _PostControlState extends State<PostControl> {
|
||||||
}
|
}
|
||||||
return Column(children: widgets);
|
return Column(children: widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildListView(
|
||||||
|
BuildContext context,
|
||||||
|
List<FlattenedTreeItem> items,
|
||||||
|
TimelineManager manager,
|
||||||
|
) {
|
||||||
|
final int count;
|
||||||
|
final int offset;
|
||||||
|
if (hasComments && showComments) {
|
||||||
|
count = items.length + 1;
|
||||||
|
offset = 1;
|
||||||
|
final scrollToIndex =
|
||||||
|
items.indexWhere((e) => e.timelineEntry.id == widget.scrollToId);
|
||||||
|
Future.delayed(
|
||||||
|
const Duration(seconds: 1),
|
||||||
|
() async => itemScrollController.jumpTo(index: scrollToIndex),
|
||||||
|
);
|
||||||
|
} else if (hasComments) {
|
||||||
|
count = 2;
|
||||||
|
offset = 0;
|
||||||
|
} else {
|
||||||
|
count = 1;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
return ScrollablePositionedList.builder(
|
||||||
|
itemCount: count,
|
||||||
|
itemScrollController: itemScrollController,
|
||||||
|
itemPositionsListener: itemPositionsListener,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == 0) {
|
||||||
|
return FlattenedTreeEntryControl(
|
||||||
|
originalItem: items.first,
|
||||||
|
openRemote: widget.openRemote,
|
||||||
|
showStatusOpenButton: widget.showStatusOpenButton,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (index == 1 && hasComments) {
|
||||||
|
return TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
setState(() {
|
||||||
|
showComments = !showComments;
|
||||||
|
});
|
||||||
|
if (showComments) {
|
||||||
|
await manager.refreshStatusChain(entry.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child:
|
||||||
|
Text(showComments ? 'Hide Comments' : 'Load & Show Comments'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return FlattenedTreeEntryControl(
|
||||||
|
originalItem: items[index - offset],
|
||||||
|
openRemote: widget.openRemote,
|
||||||
|
showStatusOpenButton: widget.showStatusOpenButton,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,10 @@ class TimelinePanel extends StatelessWidget {
|
||||||
'Building item: $itemIndex: ${item.entry.toShortString()}');
|
'Building item: $itemIndex: ${item.entry.toShortString()}');
|
||||||
return PostControl(
|
return PostControl(
|
||||||
originalItem: item,
|
originalItem: item,
|
||||||
|
scrollToId: item.id,
|
||||||
openRemote: false,
|
openRemote: false,
|
||||||
showStatusOpenButton: true,
|
showStatusOpenButton: true,
|
||||||
|
isRoot: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (context, index) => Divider(),
|
separatorBuilder: (context, index) => Divider(),
|
||||||
|
|
|
@ -137,9 +137,11 @@ final appRouter = GoRouter(
|
||||||
EditorScreen(id: state.params['id'] ?? 'Not Found'),
|
EditorScreen(id: state.params['id'] ?? 'Not Found'),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'view/:id',
|
path: 'view/:id/:goto_id',
|
||||||
builder: (context, state) =>
|
builder: (context, state) => PostScreen(
|
||||||
PostScreen(id: state.params['id'] ?? 'Not Found'),
|
id: state.params['id'] ?? 'Not Found',
|
||||||
|
goToId: state.params['goto_id'] ?? 'Not Found',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
|
|
|
@ -7,7 +7,13 @@ import '../services/timeline_manager.dart';
|
||||||
class PostScreen extends StatelessWidget {
|
class PostScreen extends StatelessWidget {
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
const PostScreen({super.key, required this.id});
|
final String goToId;
|
||||||
|
|
||||||
|
const PostScreen({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
required this.goToId,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -17,13 +23,12 @@ class PostScreen extends StatelessWidget {
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await manager.refreshStatusChain(id);
|
await manager.refreshStatusChain(id);
|
||||||
},
|
},
|
||||||
child: SingleChildScrollView(
|
child: PostControl(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
originalItem: post,
|
||||||
child: PostControl(
|
scrollToId: goToId,
|
||||||
originalItem: post,
|
openRemote: true,
|
||||||
openRemote: true,
|
showStatusOpenButton: true,
|
||||||
showStatusOpenButton: true,
|
isRoot: true,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onError: (error) => Text('Error getting post: $error'));
|
onError: (error) => Text('Error getting post: $error'));
|
||||||
|
|
|
@ -8,7 +8,7 @@ import Foundation
|
||||||
import desktop_window
|
import desktop_window
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import path_provider_macos
|
import path_provider_macos
|
||||||
import shared_preferences_macos
|
import shared_preferences_foundation
|
||||||
import sqflite
|
import sqflite
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ PODS:
|
||||||
- FMDB/standard (2.7.5)
|
- FMDB/standard (2.7.5)
|
||||||
- path_provider_macos (0.0.1):
|
- path_provider_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- shared_preferences_macos (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqflite (0.0.2):
|
- sqflite (0.0.2):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
@ -22,7 +23,7 @@ DEPENDENCIES:
|
||||||
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
||||||
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
|
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`)
|
||||||
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
|
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
|
|
||||||
|
@ -39,8 +40,8 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
path_provider_macos:
|
path_provider_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
||||||
shared_preferences_macos:
|
shared_preferences_foundation:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos
|
||||||
sqflite:
|
sqflite:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
|
@ -51,8 +52,8 @@ SPEC CHECKSUMS:
|
||||||
flutter_secure_storage_macos: 75c8cadfdba05ca007c0fa4ea0c16e5cf85e521b
|
flutter_secure_storage_macos: 75c8cadfdba05ca007c0fa4ea0c16e5cf85e521b
|
||||||
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
|
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
|
||||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
path_provider_macos: 05fb0ef0cedf3e5bd179b9e41a638682b37133ea
|
||||||
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
|
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
|
||||||
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
|
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
|
||||||
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
||||||
|
|
||||||
|
|
38
pubspec.lock
38
pubspec.lock
|
@ -147,7 +147,7 @@ packages:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.4"
|
version: "5.2.5"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -260,7 +260,7 @@ packages:
|
||||||
name: flutter_widget_from_html_core
|
name: flutter_widget_from_html_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.0+2"
|
version: "0.9.1"
|
||||||
functional_listener:
|
functional_listener:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -323,7 +323,7 @@ packages:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.2"
|
version: "3.3.0"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -351,7 +351,7 @@ packages:
|
||||||
name: image_picker_ios
|
name: image_picker_ios
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.6+4"
|
version: "0.8.6+5"
|
||||||
image_picker_platform_interface:
|
image_picker_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -484,7 +484,7 @@ packages:
|
||||||
name: path_provider_macos
|
name: path_provider_macos
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.0.7"
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -562,13 +562,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.27.7"
|
version: "0.27.7"
|
||||||
|
scrollable_positioned_list:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: scrollable_positioned_list
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.5"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.15"
|
version: "2.0.16"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -576,10 +583,10 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.14"
|
version: "2.0.14"
|
||||||
shared_preferences_ios:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_ios
|
name: shared_preferences_foundation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
@ -590,13 +597,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
shared_preferences_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_macos
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.4"
|
|
||||||
shared_preferences_platform_interface:
|
shared_preferences_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -636,14 +636,14 @@ packages:
|
||||||
name: sqflite
|
name: sqflite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.2"
|
version: "2.2.3"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqflite_common
|
name: sqflite_common
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0+2"
|
version: "2.4.1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -678,7 +678,7 @@ packages:
|
||||||
name: synchronized
|
name: synchronized
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0+3"
|
version: "3.0.1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -825,7 +825,7 @@ packages:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+2"
|
version: "0.2.0+3"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -37,6 +37,7 @@ dependencies:
|
||||||
flutter_file_dialog: ^2.3.2
|
flutter_file_dialog: ^2.3.2
|
||||||
multi_trigger_autocomplete: ^0.1.1
|
multi_trigger_autocomplete: ^0.1.1
|
||||||
video_player: ^2.4.10
|
video_player: ^2.4.10
|
||||||
|
scrollable_positioned_list: ^0.3.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue