From 8bd2b8de3d5f6de3043de8378989250c734b264a Mon Sep 17 00:00:00 2001 From: Katarina Sheremet Date: Mon, 24 Aug 2020 17:45:59 +0200 Subject: [PATCH 1/7] Use InteractiveViewer for comics --- lib/view/comic_page.dart | 109 +++++++------- lib/view/helpers/strip_image.dart | 24 ++- pubspec.lock | 235 ++++++++++++++++++++---------- 3 files changed, 221 insertions(+), 147 deletions(-) diff --git a/lib/view/comic_page.dart b/lib/view/comic_page.dart index a0bd1f3..9ad1bba 100644 --- a/lib/view/comic_page.dart +++ b/lib/view/comic_page.dart @@ -214,63 +214,66 @@ class _StripPageState extends State { builder: (context, snapshot) { if (snapshot.hasData) { _controller = PageController(initialPage: snapshot.data); - return PageView.builder( - physics: const AlwaysScrollableScrollPhysics(), - controller: _controller, - itemCount: widget.viewModel.stripIds.length, - itemBuilder: (context, i) => FutureBuilder( - future: Provide.value(context) - .getStrip( - widget.viewModel.comic, - widget.viewModel.stripIds.elementAt(i), - allowFromCache: _allowCache, - prefetch: widget.viewModel.stripIds.sublist( - max(0, i - 2), - min(widget.viewModel.stripIds.length - 1, i + 5), - ), - ) - .first, - builder: (context, stripSnapshot) { - if (stripSnapshot.hasData) { - _allowCache = true; - if (stripSnapshot.data.imageBytes == null) { - var title = stripSnapshot.data.title ?? 'n/a'; - if (stripSnapshot.hasError) { - title += ' (${stripSnapshot.error})'; - } - return Center( - child: Text( - 'Данная страница ' - '${widget.viewModel.stripIds.elementAt(i)} еще не ' - 'поддерживается мобильным приложением: $title.', + return InteractiveViewer( + maxScale: 5, + child: PageView.builder( + physics: const ClampingScrollPhysics(), + controller: _controller, + itemCount: widget.viewModel.stripIds.length, + itemBuilder: (context, i) => FutureBuilder( + future: Provide.value(context) + .getStrip( + widget.viewModel.comic, + widget.viewModel.stripIds.elementAt(i), + allowFromCache: _allowCache, + prefetch: widget.viewModel.stripIds.sublist( + max(0, i - 2), + min(widget.viewModel.stripIds.length - 1, i + 5), ), - ); - } else { - if (!_isOrientationSetup) { - setUpOrientation(stripSnapshot.data.imageBytes); + ) + .first, + builder: (context, stripSnapshot) { + if (stripSnapshot.hasData) { + _allowCache = true; + if (stripSnapshot.data.imageBytes == null) { + var title = stripSnapshot.data.title ?? 'n/a'; + if (stripSnapshot.hasError) { + title += ' (${stripSnapshot.error})'; + } + return Center( + child: Text( + 'Данная страница ' + '${widget.viewModel.stripIds.elementAt(i)} еще не ' + 'поддерживается мобильным приложением: $title.', + ), + ); + } else { + if (!_isOrientationSetup) { + setUpOrientation(stripSnapshot.data.imageBytes); + } + widget.viewModel.currentStrip = stripSnapshot.data; + widget.viewModel.currentStripId = + widget.viewModel.stripIds.elementAt(i); + // TODO(ksheremet): Zoomable widget doesn't work + // in Column + return StripImage( + viewModel: widget.viewModel, + ); } - widget.viewModel.currentStrip = stripSnapshot.data; - widget.viewModel.currentStripId = - widget.viewModel.stripIds.elementAt(i); - // TODO(ksheremet): Zoomable widget doesn't work - // in Column - return StripImage( - viewModel: widget.viewModel, - ); + } else { + return const Center(child: CircularProgressIndicator()); } - } else { - return const Center(child: CircularProgressIndicator()); - } - }), - onPageChanged: (index) { - FirebaseAnalytics().logViewItem( - itemCategory: widget.viewModel.comic.id, - itemId: index.toString(), - itemName: index.toString(), - ); + }), + onPageChanged: (index) { + FirebaseAnalytics().logViewItem( + itemCategory: widget.viewModel.comic.id, + itemId: index.toString(), + itemName: index.toString(), + ); - widget.viewModel.setLastSeenPage(index); - }, + widget.viewModel.setLastSeenPage(index); + }, + ), ); } else { return const CircularProgressIndicator(); diff --git a/lib/view/helpers/strip_image.dart b/lib/view/helpers/strip_image.dart index e9ea51f..f520fe5 100644 --- a/lib/view/helpers/strip_image.dart +++ b/lib/view/helpers/strip_image.dart @@ -1,6 +1,5 @@ import 'package:comicslate/view_model/comic_page_view_model.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_advanced_networkimage/zoomable.dart'; class StripImage extends StatelessWidget { final ComicPageViewModel viewModel; @@ -13,19 +12,14 @@ class StripImage extends StatelessWidget { if (viewModel.currentStrip.title != null) { aboutStrip = aboutStrip + viewModel.currentStrip.title; } - return ZoomableWidget( - enableRotate: false, - maxScale: 3, - zoomSteps: 2, - multiFingersPan: true, - singleFingerPan: false, - minScale: 1, - child: Column(children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 5), - child: Text(aboutStrip), - ), - Expanded(child: Image.memory(viewModel.currentStrip.imageBytes)), - ])); + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Text(aboutStrip), + ), + Expanded(child: Image.memory(viewModel.currentStrip.imageBytes)), + ], + ); } } diff --git a/pubspec.lock b/pubspec.lock index d56b7f1..680eb32 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "7.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.39.8" + version: "0.39.17" args: dependency: transitive description: @@ -28,21 +28,21 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.1" + version: "2.5.0-nullsafety" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety" build: dependency: transitive description: name: build url: "https://pub.dartlang.org" source: hosted - version: "1.2.2" + version: "1.3.0" build_config: dependency: transitive description: @@ -63,21 +63,21 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.3.7" + version: "1.3.11" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.9.0" + version: "1.10.2" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "5.1.0" + version: "6.0.1" built_collection: dependency: transitive description: @@ -99,13 +99,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "7.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.2" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "1.2.0-nullsafety" checked_yaml: dependency: transitive description: @@ -113,27 +120,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.1.0-nullsafety" code_builder: dependency: transitive description: name: code_builder url: "https://pub.dartlang.org" source: hosted - version: "3.2.1" + version: "3.4.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.12" + version: "1.15.0-nullsafety.2" convert: dependency: transitive description: @@ -147,21 +161,21 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "0.13.9" + version: "0.14.0" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.5" csslib: dependency: transitive description: name: csslib url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" + version: "0.16.2" dart_style: dependency: transitive description: @@ -175,28 +189,56 @@ packages: name: device_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.2+3" + version: "0.4.2+7" + device_info_platform_interface: + dependency: transitive + description: + name: device_info_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.0-nullsafety" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "5.1.0" + version: "5.2.1" + firebase: + dependency: transitive + description: + name: firebase + url: "https://pub.dartlang.org" + source: hosted + version: "7.3.0" firebase_analytics: dependency: "direct main" description: name: firebase_analytics url: "https://pub.dartlang.org" source: hosted - version: "5.0.11" + version: "5.0.16" + firebase_analytics_platform_interface: + dependency: transitive + description: + name: firebase_analytics_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + firebase_analytics_web: + dependency: transitive + description: + name: firebase_analytics_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" fixnum: dependency: transitive description: @@ -222,7 +264,7 @@ packages: name: flutter_cache_manager url: "https://pub.dartlang.org" source: hosted - version: "1.2.2" + version: "1.4.1" flutter_localizations: dependency: "direct main" description: flutter @@ -234,7 +276,7 @@ packages: name: flutter_sentry url: "https://pub.dartlang.org" source: hosted - version: "0.4.0" + version: "0.4.4" flutter_svg: dependency: transitive description: @@ -279,7 +321,7 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.1" + version: "0.12.2" http_multi_server: dependency: transitive description: @@ -314,7 +356,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.1+1" + version: "0.6.3-nullsafety" json_annotation: dependency: transitive description: @@ -335,49 +377,42 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.6" + version: "0.12.10-nullsafety" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.8" + version: "1.3.0-nullsafety.2" mime: dependency: transitive description: name: mime url: "https://pub.dartlang.org" source: hosted - version: "0.9.6+3" - multi_server_socket: - dependency: transitive - description: - name: multi_server_socket - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" + version: "0.9.7" node_interop: dependency: transitive description: name: node_interop url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.1.1" node_io: dependency: transitive description: name: node_io url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" node_preamble: dependency: transitive description: name: node_preamble url: "https://pub.dartlang.org" source: hosted - version: "1.4.8" + version: "1.4.12" package_config: dependency: transitive description: @@ -391,21 +426,21 @@ packages: name: package_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.0+17" + version: "0.4.3" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety" path_drawing: dependency: transitive description: name: path_drawing url: "https://pub.dartlang.org" source: hosted - version: "0.4.1" + version: "0.4.1+1" path_parsing: dependency: transitive description: @@ -419,35 +454,42 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.7" + version: "1.6.14" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+2" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+2" + version: "0.0.4+3" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.3" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.9.0" + version: "1.10.0-nullsafety" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "3.1.0" platform: dependency: transitive description: @@ -455,6 +497,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1" + platform_detect: + dependency: transitive + description: + name: platform_detect + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" plugin_platform_interface: dependency: transitive description: @@ -468,7 +517,14 @@ packages: name: pool url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.5.0-nullsafety" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.13" provide: dependency: "direct main" description: @@ -503,7 +559,7 @@ packages: name: rxdart url: "https://pub.dartlang.org" source: hosted - version: "0.24.0" + version: "0.24.1" sentry: dependency: transitive description: @@ -517,42 +573,49 @@ packages: name: share url: "https://pub.dartlang.org" source: hosted - version: "0.6.4+1" + version: "0.6.4+5" shared_preferences: dependency: "direct main" description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "0.5.7+1" + version: "0.5.10" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2+2" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+8" + version: "0.0.1+10" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.0.4" shared_preferences_web: dependency: transitive description: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.2+5" + version: "0.1.2+7" shelf: dependency: transitive description: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "0.7.5" + version: "0.7.9" shelf_packages_handler: dependency: transitive description: @@ -585,56 +648,56 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "0.9.5" + version: "0.9.6" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" source_maps: dependency: transitive description: name: source_maps url: "https://pub.dartlang.org" source: hosted - version: "0.10.9" + version: "0.10.10-nullsafety" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety" sqflite: dependency: transitive description: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "1.3.0+1" + version: "1.3.1+1" sqflite_common: dependency: transitive description: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.2+1" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "1.10.0-nullsafety" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety" stream_transform: dependency: transitive description: @@ -648,42 +711,42 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0-nullsafety" synchronized: dependency: transitive description: name: synchronized url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.2.0+2" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety" test: dependency: "direct dev" description: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.14.3" + version: "1.16.0-nullsafety.2" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.2.19-nullsafety" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.3.4" + version: "0.3.12-nullsafety.2" timing: dependency: transitive description: @@ -697,70 +760,77 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.3.0-nullsafety.2" url_launcher: dependency: "direct main" description: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.4.5" + version: "5.5.0" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+5" + version: "0.0.1+7" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.6" + version: "1.0.7" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.1+4" + version: "0.1.2+1" usage: dependency: transitive description: name: usage url: "https://pub.dartlang.org" source: hosted - version: "3.4.1" + version: "3.4.2" uuid: dependency: transitive description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.2.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.1.0-nullsafety.2" vm_service: dependency: transitive description: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "4.0.3" + version: "4.2.0" wakelock: dependency: "direct main" description: name: wakelock url: "https://pub.dartlang.org" source: hosted - version: "0.1.4+1" + version: "0.1.4+2" watcher: dependency: transitive description: @@ -781,14 +851,21 @@ packages: name: webkit_inspection_protocol url: "https://pub.dartlang.org" source: hosted - version: "0.5.3" + version: "0.7.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.6.1" + version: "3.7.0" yaml: dependency: transitive description: @@ -797,5 +874,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.7.0 <3.0.0" + dart: ">=2.10.0-0.0.dev <2.10.0" flutter: ">=1.12.13+hotfix.5 <2.0.0" From 3f485c4968952563b2e1fc9711a5ebc07f185474 Mon Sep 17 00:00:00 2001 From: Katarina Sheremet Date: Tue, 25 Aug 2020 09:55:28 +0200 Subject: [PATCH 2/7] Delete advanced network image package (deprecated) --- lib/view/comic_page.dart | 5 +-- lib/view/helpers/comic_card.dart | 12 +++---- pubspec.lock | 59 ++++++++++---------------------- pubspec.yaml | 2 +- 4 files changed, 27 insertions(+), 51 deletions(-) diff --git a/lib/view/comic_page.dart b/lib/view/comic_page.dart index 9ad1bba..ec5bb87 100644 --- a/lib/view/comic_page.dart +++ b/lib/view/comic_page.dart @@ -243,8 +243,9 @@ class _StripPageState extends State { return Center( child: Text( 'Данная страница ' - '${widget.viewModel.stripIds.elementAt(i)} еще не ' - 'поддерживается мобильным приложением: $title.', + '${widget.viewModel.stripIds.elementAt(i)} еще ' + 'не поддерживается мобильным приложением: ' + '$title.', ), ); } else { diff --git a/lib/view/helpers/comic_card.dart b/lib/view/helpers/comic_card.dart index b0083fe..120cd7c 100644 --- a/lib/view/helpers/comic_card.dart +++ b/lib/view/helpers/comic_card.dart @@ -1,7 +1,6 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:comicslate/models/comic.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_advanced_networkimage/provider.dart'; -import 'package:flutter_advanced_networkimage/transition.dart'; @immutable class ComicsRatingWidget extends StatefulWidget { @@ -111,13 +110,10 @@ class ComicCard extends StatelessWidget { } else { cover = Column(children: [ Expanded( - child: TransitionToImage( - image: AdvancedNetworkImage( - imageUrl.toString(), - useDiskCache: true, - ), + child: CachedNetworkImage( + imageUrl: imageUrl.toString(), fit: BoxFit.cover, - placeholder: Image.asset('images/favicon.webp'), + placeholder: (_, __) => Image.asset('images/favicon.webp'), ), ), Container( diff --git a/pubspec.lock b/pubspec.lock index 680eb32..973299e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -99,6 +99,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "7.1.0" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" characters: dependency: transitive description: @@ -251,13 +258,13 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_advanced_networkimage: - dependency: "direct main" + flutter_blurhash: + dependency: transitive description: - name: flutter_advanced_networkimage + name: flutter_blurhash url: "https://pub.dartlang.org" source: hosted - version: "0.7.0" + version: "0.5.0" flutter_cache_manager: dependency: "direct main" description: @@ -277,13 +284,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.4" - flutter_svg: - dependency: transitive - description: - name: flutter_svg - url: "https://pub.dartlang.org" - source: hosted - version: "0.17.4" flutter_test: dependency: "direct dev" description: flutter @@ -413,6 +413,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.4.12" + octo_image: + dependency: transitive + description: + name: octo_image + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" package_config: dependency: transitive description: @@ -434,20 +441,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0-nullsafety" - path_drawing: - dependency: transitive - description: - name: path_drawing - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.1+1" - path_parsing: - dependency: transitive - description: - name: path_parsing - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.4" path_provider: dependency: transitive description: @@ -483,13 +476,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.10.0-nullsafety" - petitparser: - dependency: transitive - description: - name: petitparser - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.0" platform: dependency: transitive description: @@ -859,13 +845,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.2" - xml: - dependency: transitive - description: - name: xml - url: "https://pub.dartlang.org" - source: hosted - version: "3.7.0" yaml: dependency: transitive description: @@ -875,4 +854,4 @@ packages: version: "2.2.1" sdks: dart: ">=2.10.0-0.0.dev <2.10.0" - flutter: ">=1.12.13+hotfix.5 <2.0.0" + flutter: ">=1.20.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f26ef33..e2442ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,8 +21,8 @@ dependencies: http: ^0.12.0 shared_preferences: ^0.5.4 built_value: ^7.1.0 - flutter_advanced_networkimage: ^0.7.0 flutter_cache_manager: ^1.2.2 + cached_network_image: ^2.2.0+1 share: ^0.6.3 quiver: ">=2.0.0 <3.0.0" wakelock: ^0.1.4 From 009034cb2a999331fdb8895c48c4b8e721bb8149 Mon Sep 17 00:00:00 2001 From: Katarina Sheremet Date: Tue, 25 Aug 2020 11:47:01 +0200 Subject: [PATCH 3/7] Use ListView instead of pager --- lib/view/comic_page.dart | 46 ++++++++----------------------- lib/view/helpers/strip_image.dart | 6 +++- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/lib/view/comic_page.dart b/lib/view/comic_page.dart index ec5bb87..580f0c0 100644 --- a/lib/view/comic_page.dart +++ b/lib/view/comic_page.dart @@ -1,12 +1,10 @@ import 'dart:math'; -import 'dart:typed_data'; import 'package:comicslate/models/comic_strip.dart'; import 'package:comicslate/models/comicslate_client.dart'; import 'package:comicslate/view/helpers/comic_page_view_model_iw.dart'; import 'package:comicslate/view/helpers/strip_image.dart'; import 'package:comicslate/view_model/comic_page_view_model.dart'; -import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -185,7 +183,6 @@ class StripPage extends StatefulWidget { class _StripPageState extends State { PageController _controller; - bool _isOrientationSetup = false; Future _lastSeenStrip; bool _allowCache = true; @@ -216,8 +213,8 @@ class _StripPageState extends State { _controller = PageController(initialPage: snapshot.data); return InteractiveViewer( maxScale: 5, - child: PageView.builder( - physics: const ClampingScrollPhysics(), + child: ListView.builder( + scrollDirection: Axis.vertical, controller: _controller, itemCount: widget.viewModel.stripIds.length, itemBuilder: (context, i) => FutureBuilder( @@ -249,9 +246,6 @@ class _StripPageState extends State { ), ); } else { - if (!_isOrientationSetup) { - setUpOrientation(stripSnapshot.data.imageBytes); - } widget.viewModel.currentStrip = stripSnapshot.data; widget.viewModel.currentStripId = widget.viewModel.stripIds.elementAt(i); @@ -265,15 +259,16 @@ class _StripPageState extends State { return const Center(child: CircularProgressIndicator()); } }), - onPageChanged: (index) { - FirebaseAnalytics().logViewItem( - itemCategory: widget.viewModel.comic.id, - itemId: index.toString(), - itemName: index.toString(), - ); + // TODO: Log pages + // onPageChanged: (index) { + // FirebaseAnalytics().logViewItem( + // itemCategory: widget.viewModel.comic.id, + // itemId: index.toString(), + // itemName: index.toString(), + // ); - widget.viewModel.setLastSeenPage(index); - }, + // widget.viewModel.setLastSeenPage(index); + // }, ), ); } else { @@ -282,25 +277,6 @@ class _StripPageState extends State { }, ); - // TODO(ksheremet): Consider more elegant solution, doesnt' work on iOS - void setUpOrientation(Uint8List imageBytes) { - final image = MemoryImage(imageBytes); - image - .resolve(createLocalImageConfiguration(context)) - .addListener(ImageStreamListener((imageInfo, synchronousCall) { - _isOrientationSetup = true; - if (imageInfo.image.width > imageInfo.image.height) { - SystemChrome.setPreferredOrientations([ - DeviceOrientation.landscapeLeft, - DeviceOrientation.landscapeRight - ]); - } else { - SystemChrome.setPreferredOrientations( - [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); - } - })); - } - @override void dispose() { // When we leave the screen enable screen rotation diff --git a/lib/view/helpers/strip_image.dart b/lib/view/helpers/strip_image.dart index f520fe5..087e889 100644 --- a/lib/view/helpers/strip_image.dart +++ b/lib/view/helpers/strip_image.dart @@ -13,12 +13,16 @@ class StripImage extends StatelessWidget { aboutStrip = aboutStrip + viewModel.currentStrip.title; } return Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 5), child: Text(aboutStrip), ), - Expanded(child: Image.memory(viewModel.currentStrip.imageBytes)), + Image.memory( + viewModel.currentStrip.imageBytes, + fit: BoxFit.fitHeight, + ), ], ); } From 6c875eeff85b8f62970ea69ad944b77fd830478d Mon Sep 17 00:00:00 2001 From: Katarina Sheremet Date: Wed, 26 Aug 2020 15:26:07 +0200 Subject: [PATCH 4/7] Increase featching timeout to 10s --- lib/models/storage.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/models/storage.dart b/lib/models/storage.dart index b952ac3..326cdea 100644 --- a/lib/models/storage.dart +++ b/lib/models/storage.dart @@ -76,7 +76,7 @@ class FlutterCachingAPIClient extends BaseCacheManager try { await _get(url, headers: headers) .last - .timeout(const Duration(seconds: 5)); + .timeout(const Duration(seconds: 10)); } finally { _currentlyPrefetching.remove(url); } From 7465c5375e202818cd7ff39e478c3e981d42df1e Mon Sep 17 00:00:00 2001 From: Katarina Sheremet Date: Wed, 26 Aug 2020 16:55:25 +0200 Subject: [PATCH 5/7] Use provider package --- lib/main.dart | 27 +++++++++++++++------------ lib/view/comic_list.dart | 6 ++---- lib/view/comic_page.dart | 16 +++------------- pubspec.lock | 13 ++++++++++--- pubspec.yaml | 2 +- 5 files changed, 31 insertions(+), 33 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index ba2b265..3e523ed 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,7 +12,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_sentry/flutter_sentry.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/intl.dart'; -import 'package:provide/provide.dart'; +import 'package:provider/provider.dart'; void main() { FlutterSentry.wrap(() { @@ -21,18 +21,21 @@ void main() { // // In this case, the ComicslateClient gets instantiated // the first time someone uses it, and lives as a singleton after that. - final providers = Providers() - ..provide(Provider.value(ComicslateClient( - language: 'ru', - offlineStorage: FlutterCachingAPIClient( - cacheName: 'comicslate-client-json-v1', - responseParser: (js) => json.decode(utf8.decode(js))), - prefetchCache: FlutterCachingAPIClient( - cacheName: 'comicslate-client-images', - responseParser: (bytes) => bytes), - ))); + final providers = [ + Provider( + create: (_) => ComicslateClient( + language: 'ru', + offlineStorage: FlutterCachingAPIClient( + cacheName: 'comicslate-client-json', + responseParser: (js) => json.decode(utf8.decode(js))), + prefetchCache: FlutterCachingAPIClient( + cacheName: 'comicslate-client-images', + responseParser: (bytes) => bytes), + ), + ), + ]; - runApp(ProviderNode(providers: providers, child: MyApp())); + runApp(MultiProvider(providers: providers, child: MyApp())); }, dsn: 'https://b150cab29afe42278804731d11f2af9b@o336071.ingest.sentry.io/5230711'); } diff --git a/lib/view/comic_list.dart b/lib/view/comic_list.dart index 72a0bd0..b5408ab 100644 --- a/lib/view/comic_list.dart +++ b/lib/view/comic_list.dart @@ -12,7 +12,7 @@ import 'package:comicslate/view_model/comic_page_view_model.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:provide/provide.dart'; +import 'package:provider/provider.dart'; class _ComicslateTitleWidget extends StatelessWidget { static const _numberOfLogos = 9; @@ -30,9 +30,7 @@ class _ComicslateTitleWidget extends StatelessWidget { class ComicList extends StatelessWidget { @override Widget build(BuildContext context) { - // TODO(ksheremet): Create Inherited widget for bloc. Consider to try - // ScopedModel, Provide - final bloc = ComicListBloc(Provide.value(context)); + final bloc = ComicListBloc(context.watch()); return Scaffold( appBar: SearchBarWidget( title: _ComicslateTitleWidget(), diff --git a/lib/view/comic_page.dart b/lib/view/comic_page.dart index 580f0c0..51c5f6b 100644 --- a/lib/view/comic_page.dart +++ b/lib/view/comic_page.dart @@ -6,10 +6,9 @@ import 'package:comicslate/view/helpers/comic_page_view_model_iw.dart'; import 'package:comicslate/view/helpers/strip_image.dart'; import 'package:comicslate/view_model/comic_page_view_model.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; -import 'package:provide/provide.dart'; +import 'package:provider/provider.dart'; import 'package:share/share.dart'; import 'package:wakelock/wakelock.dart'; @@ -78,7 +77,7 @@ class ComicPage extends StatelessWidget { ), // Get a list of stripsId body: FutureBuilder>( - future: Provide.value(context) + future: Provider.of(context) .getStoryStripsList( ComicPageViewModelWidget.of(context).viewModel.comic) .first, @@ -218,7 +217,7 @@ class _StripPageState extends State { controller: _controller, itemCount: widget.viewModel.stripIds.length, itemBuilder: (context, i) => FutureBuilder( - future: Provide.value(context) + future: Provider.of(context) .getStrip( widget.viewModel.comic, widget.viewModel.stripIds.elementAt(i), @@ -249,8 +248,6 @@ class _StripPageState extends State { widget.viewModel.currentStrip = stripSnapshot.data; widget.viewModel.currentStripId = widget.viewModel.stripIds.elementAt(i); - // TODO(ksheremet): Zoomable widget doesn't work - // in Column return StripImage( viewModel: widget.viewModel, ); @@ -279,13 +276,6 @@ class _StripPageState extends State { @override void dispose() { - // When we leave the screen enable screen rotation - SystemChrome.setPreferredOrientations([ - DeviceOrientation.portraitUp, - DeviceOrientation.portraitDown, - DeviceOrientation.landscapeLeft, - DeviceOrientation.landscapeRight, - ]); Wakelock.disable(); super.dispose(); } diff --git a/pubspec.lock b/pubspec.lock index 973299e..924f440 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -392,6 +392,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.9.7" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" node_interop: dependency: transitive description: @@ -511,13 +518,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.13" - provide: + provider: dependency: "direct main" description: - name: provide + name: provider url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "4.3.2+1" pub_semver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e2442ce..e282f8b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,8 +30,8 @@ dependencies: package_info: ^0.4.0 url_launcher: ^5.4.5 device_info: ^0.4.1 - provide: ^1.0.2 flutter_sentry: ^0.4.0 + provider: ^4.3.2+1 dev_dependencies: flutter_test: From 844735094877701b779f21f28319f039f9b3a592 Mon Sep 17 00:00:00 2001 From: Katarina Sheremet Date: Wed, 26 Aug 2020 17:15:56 +0200 Subject: [PATCH 6/7] Add paddings to comics strip list --- lib/view/comic_page.dart | 105 ++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/lib/view/comic_page.dart b/lib/view/comic_page.dart index 51c5f6b..7e1f923 100644 --- a/lib/view/comic_page.dart +++ b/lib/view/comic_page.dart @@ -211,61 +211,66 @@ class _StripPageState extends State { if (snapshot.hasData) { _controller = PageController(initialPage: snapshot.data); return InteractiveViewer( + minScale: 0.1, maxScale: 5, - child: ListView.builder( - scrollDirection: Axis.vertical, - controller: _controller, - itemCount: widget.viewModel.stripIds.length, - itemBuilder: (context, i) => FutureBuilder( - future: Provider.of(context) - .getStrip( - widget.viewModel.comic, - widget.viewModel.stripIds.elementAt(i), - allowFromCache: _allowCache, - prefetch: widget.viewModel.stripIds.sublist( - max(0, i - 2), - min(widget.viewModel.stripIds.length - 1, i + 5), - ), - ) - .first, - builder: (context, stripSnapshot) { - if (stripSnapshot.hasData) { - _allowCache = true; - if (stripSnapshot.data.imageBytes == null) { - var title = stripSnapshot.data.title ?? 'n/a'; - if (stripSnapshot.hasError) { - title += ' (${stripSnapshot.error})'; - } - return Center( - child: Text( - 'Данная страница ' - '${widget.viewModel.stripIds.elementAt(i)} еще ' - 'не поддерживается мобильным приложением: ' - '$title.', + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: ListView.builder( + scrollDirection: Axis.vertical, + controller: _controller, + itemCount: widget.viewModel.stripIds.length, + itemBuilder: (context, i) => FutureBuilder( + future: Provider.of(context) + .getStrip( + widget.viewModel.comic, + widget.viewModel.stripIds.elementAt(i), + allowFromCache: _allowCache, + prefetch: widget.viewModel.stripIds.sublist( + max(0, i - 2), + min(widget.viewModel.stripIds.length - 1, i + 5), ), - ); + ) + .first, + builder: (context, stripSnapshot) { + if (stripSnapshot.hasData) { + _allowCache = true; + if (stripSnapshot.data.imageBytes == null) { + var title = stripSnapshot.data.title ?? 'n/a'; + if (stripSnapshot.hasError) { + title += ' (${stripSnapshot.error})'; + } + return Center( + child: Text( + 'Данная страница ' + '${widget.viewModel.stripIds.elementAt(i)} еще ' + 'не поддерживается мобильным приложением: ' + '$title.', + ), + ); + } else { + widget.viewModel.currentStrip = stripSnapshot.data; + widget.viewModel.currentStripId = + widget.viewModel.stripIds.elementAt(i); + return StripImage( + viewModel: widget.viewModel, + ); + } } else { - widget.viewModel.currentStrip = stripSnapshot.data; - widget.viewModel.currentStripId = - widget.viewModel.stripIds.elementAt(i); - return StripImage( - viewModel: widget.viewModel, - ); + return const Center( + child: CircularProgressIndicator()); } - } else { - return const Center(child: CircularProgressIndicator()); - } - }), - // TODO: Log pages - // onPageChanged: (index) { - // FirebaseAnalytics().logViewItem( - // itemCategory: widget.viewModel.comic.id, - // itemId: index.toString(), - // itemName: index.toString(), - // ); + }), + // TODO: Log pages + // onPageChanged: (index) { + // FirebaseAnalytics().logViewItem( + // itemCategory: widget.viewModel.comic.id, + // itemId: index.toString(), + // itemName: index.toString(), + // ); - // widget.viewModel.setLastSeenPage(index); - // }, + // widget.viewModel.setLastSeenPage(index); + // }, + ), ), ); } else { From 1854987d632b3b929720c6dd42413747735cdf80 Mon Sep 17 00:00:00 2001 From: Katarina Sheremet Date: Thu, 27 Aug 2020 09:43:01 +0200 Subject: [PATCH 7/7] Use scrollable positioned list --- lib/view/comic_page.dart | 165 ++++++++++++++++++++++----------------- pubspec.lock | 7 ++ pubspec.yaml | 3 +- 3 files changed, 101 insertions(+), 74 deletions(-) diff --git a/lib/view/comic_page.dart b/lib/view/comic_page.dart index 7e1f923..d80ee94 100644 --- a/lib/view/comic_page.dart +++ b/lib/view/comic_page.dart @@ -5,10 +5,12 @@ import 'package:comicslate/models/comicslate_client.dart'; import 'package:comicslate/view/helpers/comic_page_view_model_iw.dart'; import 'package:comicslate/view/helpers/strip_image.dart'; import 'package:comicslate/view_model/comic_page_view_model.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:share/share.dart'; import 'package:wakelock/wakelock.dart'; @@ -181,9 +183,13 @@ class StripPage extends StatefulWidget { } class _StripPageState extends State { - PageController _controller; Future _lastSeenStrip; bool _allowCache = true; + int _lastSeenStripValue; + + final ItemScrollController _controller = ItemScrollController(); + final ItemPositionsListener _itemPositionsListener = + ItemPositionsListener.create(); @override void initState() { @@ -191,7 +197,10 @@ class _StripPageState extends State { Wakelock.enable(); widget.viewModel.doGoToPage.listen((page) { - _controller.jumpToPage(page); + _controller.scrollTo( + index: page, + duration: const Duration(seconds: 2), + curve: Curves.easeIn); }); widget.viewModel.doRefreshStrip.listen((_) { @@ -200,84 +209,94 @@ class _StripPageState extends State { }); }); + _itemPositionsListener.itemPositions.addListener(() { + if (_itemPositionsListener.itemPositions.value.first.index != + _lastSeenStripValue) { + _lastSeenStripValue = + _itemPositionsListener.itemPositions.value.first.index; + + FirebaseAnalytics().logViewItem( + itemCategory: widget.viewModel.comic.id, + itemId: _lastSeenStripValue.toString(), + itemName: _lastSeenStripValue.toString(), + ); + + widget.viewModel.setLastSeenPage(_lastSeenStripValue); + } + }); + _lastSeenStrip = widget.viewModel.getLastSeenPage(); + super.initState(); } @override Widget build(BuildContext context) => FutureBuilder( - future: _lastSeenStrip, - builder: (context, snapshot) { - if (snapshot.hasData) { - _controller = PageController(initialPage: snapshot.data); - return InteractiveViewer( - minScale: 0.1, - maxScale: 5, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: ListView.builder( - scrollDirection: Axis.vertical, - controller: _controller, - itemCount: widget.viewModel.stripIds.length, - itemBuilder: (context, i) => FutureBuilder( - future: Provider.of(context) - .getStrip( - widget.viewModel.comic, - widget.viewModel.stripIds.elementAt(i), - allowFromCache: _allowCache, - prefetch: widget.viewModel.stripIds.sublist( - max(0, i - 2), - min(widget.viewModel.stripIds.length - 1, i + 5), - ), - ) - .first, - builder: (context, stripSnapshot) { - if (stripSnapshot.hasData) { - _allowCache = true; - if (stripSnapshot.data.imageBytes == null) { - var title = stripSnapshot.data.title ?? 'n/a'; - if (stripSnapshot.hasError) { - title += ' (${stripSnapshot.error})'; - } - return Center( - child: Text( - 'Данная страница ' - '${widget.viewModel.stripIds.elementAt(i)} еще ' - 'не поддерживается мобильным приложением: ' - '$title.', - ), - ); - } else { - widget.viewModel.currentStrip = stripSnapshot.data; - widget.viewModel.currentStripId = - widget.viewModel.stripIds.elementAt(i); - return StripImage( - viewModel: widget.viewModel, - ); - } - } else { - return const Center( - child: CircularProgressIndicator()); + future: _lastSeenStrip, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return InteractiveViewer( + minScale: 0.1, + maxScale: 5, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: ScrollablePositionedList.builder( + initialScrollIndex: snapshot.data, + scrollDirection: Axis.vertical, + itemScrollController: _controller, + itemPositionsListener: _itemPositionsListener, + itemCount: widget.viewModel.stripIds.length, + itemBuilder: (context, i) => FutureBuilder( + future: Provider.of(context) + .getStrip( + widget.viewModel.comic, + widget.viewModel.stripIds.elementAt(i), + allowFromCache: _allowCache, + prefetch: widget.viewModel.stripIds.sublist( + max(0, i - 2), + min(widget.viewModel.stripIds.length - 1, i + 5), + ), + ) + .first, + builder: (context, stripSnapshot) { + if (stripSnapshot.hasData) { + _allowCache = true; + if (stripSnapshot.data.imageBytes == null) { + var title = stripSnapshot.data.title ?? 'n/a'; + if (stripSnapshot.hasError) { + title += ' (${stripSnapshot.error})'; } - }), - // TODO: Log pages - // onPageChanged: (index) { - // FirebaseAnalytics().logViewItem( - // itemCategory: widget.viewModel.comic.id, - // itemId: index.toString(), - // itemName: index.toString(), - // ); - - // widget.viewModel.setLastSeenPage(index); - // }, - ), - ), - ); - } else { - return const CircularProgressIndicator(); - } - }, - ); + return Center( + child: Text( + 'Данная страница ' + '${widget.viewModel.stripIds.elementAt(i)} еще ' + 'не поддерживается мобильным приложением: ' + '$title.', + ), + ); + } else { + widget.viewModel.currentStrip = stripSnapshot.data; + widget.viewModel.currentStripId = + widget.viewModel.stripIds.elementAt(i); + return StripImage( + viewModel: widget.viewModel, + ); + } + } else { + return SizedBox( + height: MediaQuery.of(context).size.height / 5, + child: + const Center(child: CircularProgressIndicator())); + } + }), + ), + ), + ); + }); @override void dispose() { diff --git a/pubspec.lock b/pubspec.lock index 924f440..bc59b07 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -553,6 +553,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.24.1" + scrollable_positioned_list: + dependency: "direct main" + description: + name: scrollable_positioned_list + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.7" sentry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e282f8b..de8642a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,8 +30,9 @@ dependencies: package_info: ^0.4.0 url_launcher: ^5.4.5 device_info: ^0.4.1 - flutter_sentry: ^0.4.0 provider: ^4.3.2+1 + flutter_sentry: ^0.4.4 + scrollable_positioned_list: ^0.1.7 dev_dependencies: flutter_test: