diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 4bf880a..55e4c09 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -1,3 +1,6 @@ +v2.3.0 +Added pull to refresh + v2.2.0 Added refresh button on error diff --git a/lib/components/sliver_persistent_header_padding.dart b/lib/components/sliver_persistent_header_padding.dart new file mode 100644 index 0000000..843d087 --- /dev/null +++ b/lib/components/sliver_persistent_header_padding.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class SliverPersistentHeaderPadding extends StatelessWidget { + final double maxHeight; + + const SliverPersistentHeaderPadding({required this.maxHeight}); + + @override + Widget build(BuildContext context) { + return SliverPersistentHeader( + delegate: _SliverPersistentHeaderPaddingDelegate(maxHeight: maxHeight,), + floating: false, + pinned: false, + ); + } +} + +class _SliverPersistentHeaderPaddingDelegate extends SliverPersistentHeaderDelegate { + final double maxHeight; + + const _SliverPersistentHeaderPaddingDelegate({required this.maxHeight}); + + @override + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { + return Container(); + } + + @override + double get maxExtent => maxHeight; + + @override + double get minExtent => 0; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { + return oldDelegate.maxExtent != maxExtent; + } +} \ No newline at end of file diff --git a/lib/pages/train_info_page/view_train/train_info.dart b/lib/pages/train_info_page/view_train/train_info.dart index d7d2aa4..d6e0e34 100644 --- a/lib/pages/train_info_page/view_train/train_info.dart +++ b/lib/pages/train_info_page/view_train/train_info.dart @@ -36,7 +36,7 @@ class TrainInfo extends StatelessWidget { return TrainInfoErrorMaterial(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,); } - return TrainInfoMaterial(trainData: snapshot.data!,); + return TrainInfoMaterial(trainData: snapshot.data!, refresh: refresh,); case UiDesign.CUPERTINO: if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) { return TrainInfoLoadingCupertino(title: trainNumber.toString(),); @@ -45,7 +45,7 @@ class TrainInfo extends StatelessWidget { return TrainInfoErrorCupertino(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,); } - return TrainInfoCupertino(trainData: snapshot.data!,); + return TrainInfoCupertino(trainData: snapshot.data!, refresh: refresh,); default: throw UnmatchedUiDesignException(uiDesign); } diff --git a/lib/pages/train_info_page/view_train/train_info_cupertino.dart b/lib/pages/train_info_page/view_train/train_info_cupertino.dart index 0a0465c..754aa97 100644 --- a/lib/pages/train_info_page/view_train/train_info_cupertino.dart +++ b/lib/pages/train_info_page/view_train/train_info_cupertino.dart @@ -1,5 +1,8 @@ +import 'dart:math'; + import 'package:flutter/cupertino.dart'; import 'package:info_tren/components/cupertino_divider.dart'; +import 'package:info_tren/components/sliver_persistent_header_padding.dart'; import 'package:info_tren/models/train_data.dart' hide State; import 'package:info_tren/models/ui_design.dart'; import 'package:info_tren/pages/train_info_page/train_info_constants.dart'; @@ -58,8 +61,9 @@ class TrainInfoErrorCupertino extends TrainInfoError { class TrainInfoCupertino extends StatelessWidget { final TrainData trainData; + final Future Function()? refresh; - TrainInfoCupertino({required this.trainData}); + TrainInfoCupertino({required this.trainData, this.refresh,}); @override Widget build(BuildContext context) { @@ -74,84 +78,153 @@ class TrainInfoCupertino extends StatelessWidget { builder: (context) { final topPadding = MediaQuery.of(context).padding.top; - return CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: Padding( - padding: EdgeInsets.only( - top: topPadding, - ), - child: Container(), - ), - ), - DisplayTrainID(trainData: trainData,), - DisplayTrainOperator(trainData: trainData,), - DisplayTrainRoute(trainData: trainData,), - DisplayTrainDeparture(trainData: trainData,), - SliverToBoxAdapter( - child: CupertinoDivider( - color: FOREGROUND_WHITE, - ), - ), - DisplayTrainLastInfo(trainData: trainData,), - SliverToBoxAdapter( - child: CupertinoDivider(), - ), - SliverToBoxAdapter( - child: IntrinsicHeight( - child: Row( - children: [ - // Expanded( - // child: DisplayTrainNextStop(trainData: trainData,), - // ), - Expanded( - child: DisplayTrainDestination(trainData: trainData,), + return NestedScrollView( + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + // SliverPadding( + // padding: EdgeInsets.only( + // top: topPadding, + // ), + // ), + SliverPersistentHeaderPadding(maxHeight: topPadding,) + ]; + }, + body: Builder( + builder: (context) { + return CustomScrollView( + slivers: [ + if (refresh != null) + CupertinoSliverRefreshControl( + builder: (context, mode, pulledExtent, refreshTriggerPullDistance, refreshIndicatorExtent) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: pulledExtent, + child: Column( + children: [ + Container( + height: min(refreshIndicatorExtent, pulledExtent), + child: Center( + child: Builder( + builder: (context) { + if (mode == RefreshIndicatorMode.inactive) { + return Container(); + } + else if (mode == RefreshIndicatorMode.done) { + return Text('Refreshed!'); + } + else if (mode == RefreshIndicatorMode.drag) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + CupertinoActivityIndicator(animating: false,), + Text('Pull to refresh...'), + ], + ); + } + else if (mode == RefreshIndicatorMode.armed) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + CupertinoActivityIndicator(animating: false,), + Text('Release to refresh...'), + ], + ); + } + else { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + CupertinoActivityIndicator(), + Text('Refreshing'), + ], + ); + } + }, + ), + ), + ), + Expanded(child: Container(),), + ], + ), + ), + ], + ); + }, + onRefresh: refresh, ), - SizedBox( - height: double.infinity, - child: CupertinoVerticalDivider(), + DisplayTrainID(trainData: trainData,), + DisplayTrainOperator(trainData: trainData,), + DisplayTrainRoute(trainData: trainData,), + DisplayTrainDeparture(trainData: trainData,), + SliverToBoxAdapter( + child: CupertinoDivider( + color: FOREGROUND_WHITE, ), - Expanded(child: DisplayTrainRouteDistance(trainData: trainData,),), - ], - ), - ), - ), - // SliverToBoxAdapter( - // child: CupertinoDivider(), - // ), - // SliverToBoxAdapter( - // child: IntrinsicHeight( - // child: Row( - // children: [ - // // Expanded( - // // child: DisplayTrainRouteDuration(trainData: trainData,), - // // ), - // Expanded(child: Container(),), - // SizedBox( - // height: double.infinity, - // child: CupertinoVerticalDivider(), - // ), - // Expanded( - // child: DisplayTrainRouteDistance(trainData: trainData,), - // ) - // ], - // ), - // ), - // ), - SliverToBoxAdapter( - child: CupertinoDivider( - color: FOREGROUND_WHITE, - ), - ), - DisplayTrainStations( - trainData: trainData, - ), - SliverToBoxAdapter( - child: Container( - height: MediaQuery.of(context).viewPadding.bottom, - ), - ), - ], + ), + DisplayTrainLastInfo(trainData: trainData,), + SliverToBoxAdapter( + child: CupertinoDivider(), + ), + SliverToBoxAdapter( + child: IntrinsicHeight( + child: Row( + children: [ + // Expanded( + // child: DisplayTrainNextStop(trainData: trainData,), + // ), + Expanded( + child: DisplayTrainDestination(trainData: trainData,), + ), + SizedBox( + height: double.infinity, + child: CupertinoVerticalDivider(), + ), + Expanded(child: DisplayTrainRouteDistance(trainData: trainData,),), + ], + ), + ), + ), + // SliverToBoxAdapter( + // child: CupertinoDivider(), + // ), + // SliverToBoxAdapter( + // child: IntrinsicHeight( + // child: Row( + // children: [ + // // Expanded( + // // child: DisplayTrainRouteDuration(trainData: trainData,), + // // ), + // Expanded(child: Container(),), + // SizedBox( + // height: double.infinity, + // child: CupertinoVerticalDivider(), + // ), + // Expanded( + // child: DisplayTrainRouteDistance(trainData: trainData,), + // ) + // ], + // ), + // ), + // ), + SliverToBoxAdapter( + child: CupertinoDivider( + color: FOREGROUND_WHITE, + ), + ), + DisplayTrainStations( + trainData: trainData, + ), + SliverToBoxAdapter( + child: Container( + height: MediaQuery.of(context).viewPadding.bottom, + ), + ), + ], + ); + } + ), ); } ), diff --git a/lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart b/lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart index 864cc30..35f574d 100644 --- a/lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart +++ b/lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart @@ -264,7 +264,7 @@ class ArrivalTime extends StatelessWidget { final now = DateTime.now(); final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]); - final newDate = oldDate.subtract(Duration(minutes: delay)); + final newDate = oldDate.add(Duration(minutes: delay)); return Column( mainAxisSize: MainAxisSize.min, @@ -398,7 +398,7 @@ class DepartureTime extends StatelessWidget { final now = DateTime.now(); final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]); - final newDate = oldDate.subtract(Duration(minutes: delay)); + final newDate = oldDate.add(Duration(minutes: delay)); return Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/train_info_page/view_train/train_info_material.dart b/lib/pages/train_info_page/view_train/train_info_material.dart index 08c11d4..3d0907c 100644 --- a/lib/pages/train_info_page/view_train/train_info_material.dart +++ b/lib/pages/train_info_page/view_train/train_info_material.dart @@ -59,8 +59,9 @@ bool isSmallScreen(BuildContext context) => MediaQuery.of(context).size.height < class TrainInfoMaterial extends StatelessWidget { final TrainData trainData; + final Future Function()? refresh; - TrainInfoMaterial({required this.trainData}); + TrainInfoMaterial({required this.trainData, this.refresh,}); @override Widget build(BuildContext context) { @@ -80,69 +81,72 @@ class TrainInfoMaterial extends StatelessWidget { Expanded( child: SafeArea( bottom: false, - child: CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: DisplayTrainID(trainData: trainData,), - ), - SliverToBoxAdapter( - child: DisplayTrainOperator(trainData: trainData,), - ), - SliverPadding( - padding: const EdgeInsets.only(left: 2, right: 2), - sliver: SliverToBoxAdapter( - child: DisplayTrainRoute(trainData: trainData,), + child: RefreshIndicator( + onRefresh: refresh ?? () async {}, + child: CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: DisplayTrainID(trainData: trainData,), ), - ), - SliverToBoxAdapter( - child: DisplayTrainDeparture(trainData: trainData,), - ), - // SliverToBoxAdapter( - // child: Divider( - // color: Colors.white70, - // height: isSmallScreen(context) ? 8 : 16, - // ), - // ), - SliverToBoxAdapter( - child: DisplayTrainLastInfo(trainData: trainData,), - ), - SliverToBoxAdapter( - child: IntrinsicHeight( - child: Row( - children: [ - // Expanded(child: DisplayTrainNextStop(trainData: trainData,)), - Expanded(child: DisplayTrainDestination(trainData: trainData,)), - Expanded(child: DisplayTrainRouteDistance(trainData: trainData,),), - ], + SliverToBoxAdapter( + child: DisplayTrainOperator(trainData: trainData,), + ), + SliverPadding( + padding: const EdgeInsets.only(left: 2, right: 2), + sliver: SliverToBoxAdapter( + child: DisplayTrainRoute(trainData: trainData,), ), ), - ), - // SliverToBoxAdapter( - // child: IntrinsicHeight( - // child: Row( - // children: [ - // // Expanded(child: DisplayTrainRouteDuration(trainData: trainData,)), - // Expanded(child: Container(),), - // Expanded(child: DisplayTrainRouteDistance(trainData: trainData,)), - // ], - // ), - // ), - // ), - SliverToBoxAdapter( - child: Divider( - color: Colors.white70, - height: isSmallScreen(context) ? 8 : 16, + SliverToBoxAdapter( + child: DisplayTrainDeparture(trainData: trainData,), ), - ), - DisplayTrainStations( - trainData: trainData, - ), - SliverToBoxAdapter( - child: Container( - height: MediaQuery.of(context).viewPadding.bottom, + // SliverToBoxAdapter( + // child: Divider( + // color: Colors.white70, + // height: isSmallScreen(context) ? 8 : 16, + // ), + // ), + SliverToBoxAdapter( + child: DisplayTrainLastInfo(trainData: trainData,), ), - ), - ], + SliverToBoxAdapter( + child: IntrinsicHeight( + child: Row( + children: [ + // Expanded(child: DisplayTrainNextStop(trainData: trainData,)), + Expanded(child: DisplayTrainDestination(trainData: trainData,)), + Expanded(child: DisplayTrainRouteDistance(trainData: trainData,),), + ], + ), + ), + ), + // SliverToBoxAdapter( + // child: IntrinsicHeight( + // child: Row( + // children: [ + // // Expanded(child: DisplayTrainRouteDuration(trainData: trainData,)), + // Expanded(child: Container(),), + // Expanded(child: DisplayTrainRouteDistance(trainData: trainData,)), + // ], + // ), + // ), + // ), + SliverToBoxAdapter( + child: Divider( + color: Colors.white70, + height: isSmallScreen(context) ? 8 : 16, + ), + ), + DisplayTrainStations( + trainData: trainData, + ), + SliverToBoxAdapter( + child: Container( + height: MediaQuery.of(context).viewPadding.bottom, + ), + ), + ], + ), ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 6153ebd..9d87aec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: O aplicație de vizualizare a datelor puse la dispoziție de Inform # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.2.0 +version: 2.3.0 environment: sdk: ">=2.12.0 <3.0.0"