From d9c415829095649d8e0193a9f0efd1cfa739d183 Mon Sep 17 00:00:00 2001 From: Felix <61414068+TruFelix@users.noreply.github.com> Date: Tue, 24 Jan 2023 19:37:17 +0100 Subject: [PATCH] Squashed commit of the following: commit 4815598bf3d21f41a4b4c8b0c926e312fde4f8f9 Author: Jonas Wanke Date: Tue Jan 24 18:24:08 2023 +0100 Release v1.0.0-alpha.11 commit e7bae6dcd274f8e65e4bd383135137c3d287bcca Author: Jonas Wanke Date: Tue Jan 24 18:18:38 2023 +0100 Add more linter rules commit 40f1b6764bf2e5fb7df9a95ea448c9a7071832a8 Author: Jonas Wanke Date: Tue Jan 24 18:16:46 2023 +0100 Update black_hole_flutter to ^1.0.0 commit ed2d0a0aeb6a57741b808088be847a430fecae6a Author: Jonas Wanke Date: Tue Jan 24 18:14:42 2023 +0100 Upgrade Dart to v2.18.0, Flutter to v3.3.0 commit 59b0eb48506cfe25aa5cda0de3bade261d502f43 Author: Jonas Wanke Date: Tue Jan 24 15:40:14 2023 +0100 Add DateScrollActivity Closes: #110 commit 8855d85690ef9f5b497fb27b28c1d9bf37734df9 Author: Jonas Wanke Date: Tue Jan 24 15:39:34 2023 +0100 Implement Diagnosticable for VisibleDateRange commit 469a0ded6acab100ad5deaae8eab0bf6b29a7544 Author: Jonas Wanke Date: Tue Jan 24 15:38:46 2023 +0100 Add DateDiagnosticsProperty commit b35b24032919a231d3d817e9afbeb816ef9a768d Author: Jonas Wanke Date: Tue Jan 24 15:38:31 2023 +0100 Fix allDayEventBorder.toString() commit 174387e8c1ad356b31dba2dbdbb4a6cf224d801b Author: Jonas Wanke Date: Tue Jan 24 15:38:09 2023 +0100 Fix formatting commit 8e0315cb4169b1a0d3c04c5411b2132ce0f2ce71 Author: Jonas Wanke Date: Tue Jan 24 12:36:34 2023 +0100 Honor maxWidth in WeekIndicator Closes: #131 commit b2f06cfe070922c23dd0aca01531cc53ba925632 Author: Jonas Wanke Date: Tue Jan 24 12:03:19 2023 +0100 Remove superfluous num type arguments commit 763661eeae0a84ab1d2af3d2cd0b05277f62c044 Author: Jonas Wanke Date: Tue Jan 24 11:51:44 2023 +0100 Fix MonthPageView's shrinkWrapped height when jumping to far-away date commit c0167c24100fdd607ffb8a8d5ed9094a536e5848 Author: Jonas Wanke Date: Tue Jan 24 11:51:04 2023 +0100 Cancel animation when jumping in Date-/TimeController Closes #135 --- CHANGELOG.md | 22 ++++- analysis_options.yaml | 11 ++- example/pubspec.lock | 98 +++++----------------- example/pubspec.yaml | 4 +- lib/src/components/now_indicator.dart | 18 ++-- lib/src/components/week_indicator.dart | 15 ++-- lib/src/date/controller.dart | 112 +++++++++++++++++++++---- lib/src/date/date_page_view.dart | 15 +++- lib/src/date/month_page_view.dart | 9 +- lib/src/date/visible_date_range.dart | 29 +++++-- lib/src/event/all_day.dart | 2 +- lib/src/time/controller.dart | 19 ++++- lib/src/utils.dart | 26 ++++++ pubspec.yaml | 9 +- 14 files changed, 260 insertions(+), 129 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 806804e..7229ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm +## 1.0.0-alpha.11 ยท 2023-01-24 + +### โš ๏ธ BREAKING CHANGES +* Add `DateScrollActivity` and subclasses. `dateController.value.activity` tells you the current activity and even the target when currently animating between dates. This is breaking because `dateController.value` now stores a `DatePageValueWithScrollActivity` instead of a `DatePageValue` ([`59b0eb4`](https://github.com/JonasWanke/timetable/commit/59b0eb48506cfe25aa5cda0de3bade261d502f43)), closes: [#110](https://github.com/JonasWanke/timetable/issues/110) + +### ๐ŸŽ‰ New Features +* add `DateDiagnosticsProperty` ([`469a0de`](https://github.com/JonasWanke/timetable/commit/469a0ded6acab100ad5deaae8eab0bf6b29a7544)) +* implement `Diagnosticable` for `VisibleDateRange` and `DatePageValue` ([`8855d85`](https://github.com/JonasWanke/timetable/commit/8855d85690ef9f5b497fb27b28c1d9bf37734df9)) + +### ๐Ÿ› Bug Fixes +* cancel ongoing animations when jumping in `DateController`/`TimeController` ([`c0167c2`](https://github.com/JonasWanke/timetable/commit/c0167c24100fdd607ffb8a8d5ed9094a536e5848)), closes: [#135](https://github.com/JonasWanke/timetable/issues/135) +* honor maximum constraints in `WeekIndicator` ([`8e0315c`](https://github.com/JonasWanke/timetable/commit/8e0315cb4169b1a0d3c04c5411b2132ce0f2ce71)), closes: [#131](https://github.com/JonasWanke/timetable/issues/131) +* fix `MonthPageView`'s shrink-wrapped height when jumping to far-away date ([`763661e`](https://github.com/JonasWanke/timetable/commit/763661eeae0a84ab1d2af3d2cd0b05277f62c044)) +* fix `allDayEventBorder.toString()` ([`b35b240`](https://github.com/JonasWanke/timetable/commit/b35b24032919a231d3d817e9afbeb816ef9a768d)) + +### ๐Ÿ“ฆ Build & CI +* upgrade to Flutter: `>=3.3.0`, Dart `>=2.18.0 <3.0.0` ([`ed2d0a0`](https://github.com/JonasWanke/timetable/commit/ed2d0a0aeb6a57741b808088be847a430fecae6a)) +* update `black_hole_flutter` to `^1.0.0` ([`40f1b67`](https://github.com/JonasWanke/timetable/commit/40f1b6764bf2e5fb7df9a95ea448c9a7071832a8)) + ## 1.0.0-alpha.10 ยท 2022-08-19 ### ๐Ÿ“œ Documentation updates diff --git a/analysis_options.yaml b/analysis_options.yaml index 132ec89..a0501b7 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -5,8 +5,6 @@ analyzer: strict-casts: true strict-inference: true strict-raw-types: true - exclude: - - lib/generated_plugin_registrant.dart linter: rules: @@ -18,6 +16,7 @@ linter: avoid_final_parameters: true avoid_implementing_value_types: true avoid_js_rounded_ints: true + avoid_multiple_declarations_per_line: true avoid_positional_boolean_parameters: true avoid_returning_this: true avoid_setters_without_getters: true @@ -28,10 +27,13 @@ linter: cancel_subscriptions: true cast_nullable_to_non_nullable: true close_sinks: true + combinators_ordering: true comment_references: true conditional_uri_does_not_exist: true directives_ordering: true + discarded_futures: true flutter_style_todos: true + invariant_booleans: true join_return_with_assignment: true no_adjacent_strings_in_list: true omit_local_variable_types: true @@ -53,9 +55,13 @@ linter: throw_in_finally: true tighten_type_of_initializing_formals: true unawaited_futures: true + unnecessary_await_in_return: false unnecessary_lambdas: true + unnecessary_null_aware_operator_on_extension_on_nullable: true unnecessary_parenthesis: true unnecessary_statements: true + unnecessary_to_list_in_spreads: true + unreachable_from_main: true unsafe_html: true use_colored_box: true use_decorated_box: true @@ -63,4 +69,5 @@ linter: use_key_in_widget_constructors: false use_setters_to_change_properties: true use_string_buffers: true + use_string_in_part_of_directives: true use_super_parameters: true diff --git a/example/pubspec.lock b/example/pubspec.lock index ade2e1d..14a9b52 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -14,7 +14,7 @@ packages: name: black_hole_flutter url: "https://pub.dartlang.org" source: hosted - version: "0.3.5" + version: "1.0.0" characters: dependency: transitive description: @@ -70,49 +70,21 @@ packages: name: debug_overlay url: "https://pub.dartlang.org" source: hosted - version: "0.2.2" + version: "0.2.5" device_info_plus: dependency: transitive description: name: device_info_plus url: "https://pub.dartlang.org" source: hosted - version: "5.0.5" - device_info_plus_linux: - dependency: transitive - description: - name: device_info_plus_linux - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.2" - device_info_plus_macos: - dependency: transitive - description: - name: device_info_plus_macos - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.2" + version: "8.0.0" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" - device_info_plus_web: - dependency: transitive - description: - name: device_info_plus_web - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.2" - device_info_plus_windows: - dependency: transitive - description: - name: device_info_plus_windows - url: "https://pub.dartlang.org" - source: hosted - version: "5.0.2" + version: "7.0.0" ffi: dependency: transitive description: @@ -176,7 +148,7 @@ packages: name: implicitly_animated_list url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" intl: dependency: transitive description: @@ -211,7 +183,7 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.13" + version: "0.12.14" material_color_utilities: dependency: transitive description: @@ -232,42 +204,14 @@ packages: name: package_info_plus url: "https://pub.dartlang.org" source: hosted - version: "1.4.3+1" - package_info_plus_linux: - dependency: transitive - description: - name: package_info_plus_linux - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.5" - package_info_plus_macos: - dependency: transitive - description: - name: package_info_plus_macos - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" + version: "3.0.2" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" - package_info_plus_web: - dependency: transitive - description: - name: package_info_plus_web - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.6" - package_info_plus_windows: - dependency: transitive - description: - name: package_info_plus_windows - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" + version: "2.0.1" path: dependency: transitive description: @@ -288,14 +232,7 @@ packages: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" - remind_timetable: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "1.0.0-alpha.13" + version: "3.2.1" sensors_plus: dependency: transitive description: @@ -323,7 +260,7 @@ packages: name: shake url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" sky_engine: dependency: transitive description: flutter @@ -349,7 +286,7 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.2.0" term_glyph: dependency: transitive description: @@ -371,6 +308,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.3.0" + timetable: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.0-alpha.11" tuple: dependency: transitive description: @@ -398,7 +342,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.1.3" sdks: - dart: ">=2.18.6 <3.0.0" - flutter: ">=3.0.0" + dart: ">=2.18.0 <3.0.0" + flutter: ">=3.3.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index f97c704..ea623ba 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,8 +3,8 @@ description: A simple example app for the timetable package publish_to: none environment: - sdk: '>=2.17.0-266.8.beta <3.0.0' - flutter: '>=2.13.0-0.4.pre' + sdk: '>=2.18.0 <3.0.0' + flutter: '>=3.3.0' dependencies: debug_overlay: ^0.2.1 diff --git a/lib/src/components/now_indicator.dart b/lib/src/components/now_indicator.dart index 34a3150..7394044 100644 --- a/lib/src/components/now_indicator.dart +++ b/lib/src/components/now_indicator.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:ui'; import 'package:async/async.dart'; @@ -314,7 +315,7 @@ class _NowIndicatorPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { - _repaint?.cancel(); + unawaited(_repaint?.cancel()); _repaint = null; final pageValue = controller.value; @@ -340,11 +341,14 @@ class _NowIndicatorPainter extends CustomPainter { final maxDistance = 0.5 / devicePixelRatio; final delay = 1.days * (maxDistance / size.height); _repaint = CancelableOperation.fromFuture( - Future.delayed(delay).then((_) { - // [ChangeNotifier.notifyListeners] is protected, so we use a - // [ValueNotifier] and always set a different time. - _repaintNotifier.value = DateTimeTimetable.now(); - }), + Future.delayed( + delay, + () { + // [ChangeNotifier.notifyListeners] is protected, so we use a + // [ValueNotifier] and always set a different time. + _repaintNotifier.value = DateTimeTimetable.now(); + }, + ), ); } @@ -362,7 +366,7 @@ class _NowIndicatorPainter extends CustomPainter { void removeListener(VoidCallback listener) { _activeListenerCount--; if (_activeListenerCount == 0) { - _repaint?.cancel(); + unawaited(_repaint?.cancel()); _repaint = null; } super.removeListener(listener); diff --git a/lib/src/components/week_indicator.dart b/lib/src/components/week_indicator.dart index 974db5c..5a7baa0 100644 --- a/lib/src/components/week_indicator.dart +++ b/lib/src/components/week_indicator.dart @@ -203,19 +203,19 @@ class _RenderWeekIndicatorText extends RenderBox { @override double computeMinIntrinsicWidth(double height) => - _labelPainters.map((it) => it.width).min.toDouble(); + _labelPainters.map((it) => it.width).min.toDouble(); @override double computeMaxIntrinsicWidth(double height) { - final widths = _labelPainters.map((it) => it.width); + final widths = _labelPainters.map((it) => it.width); return (alwaysUseNarrowestVariant ? widths.min : widths.max).toDouble(); } @override double computeMinIntrinsicHeight(double width) => - _labelPainters.map((it) => it.height).min.toDouble(); + _labelPainters.map((it) => it.height).min.toDouble(); @override double computeMaxIntrinsicHeight(double width) { - final heights = _labelPainters.map((it) => it.height); + final heights = _labelPainters.map((it) => it.height); return (alwaysUseNarrowestVariant ? heights.min : heights.max).toDouble(); } @@ -238,13 +238,12 @@ class _RenderWeekIndicatorText extends RenderBox { .firstOrNull ?? narrowestPainter(); } - size = _labelPainter.size; + size = constraints.constrain(_labelPainter.size); } @override - void paint(PaintingContext context, Offset offset) { - _labelPainter.paint(context.canvas, offset); - } + void paint(PaintingContext context, Offset offset) => + _labelPainter.paint(context.canvas, offset); } /// Defines visual properties for [WeekIndicator]. diff --git a/lib/src/date/controller.dart b/lib/src/date/controller.dart index c294722..013eb71 100644 --- a/lib/src/date/controller.dart +++ b/lib/src/date/controller.dart @@ -13,19 +13,21 @@ import 'visible_date_range.dart'; /// /// To programmatically change the visible dates, use any of the following /// functions: +/// /// * [animateToToday], [animateTo], or [animateToPage] if you want an animation /// * [jumpToToday], [jumpTo], or [jumpToPage] if you don't want an animation /// /// You can also get and update the [VisibleDateRange] via [visibleRange]. -class DateController extends ValueNotifier { +class DateController extends ValueNotifier { DateController({ DateTime? initialDate, VisibleDateRange? visibleRange, }) : assert(initialDate.debugCheckIsValidTimetableDate()), // We set the correct value in the body below. - super(DatePageValue( + super(DatePageValueWithScrollActivity( visibleRange ?? VisibleDateRange.week(), 0, + const IdleDateScrollActivity(), )) { // The correct value is set via the listener when we assign to our value. _date = ValueNotifier(DateTimeTimetable.dateFromPage(0)); @@ -36,8 +38,9 @@ class DateController extends ValueNotifier { addListener(() => _visibleDates.value = value.visibleDates); final rawStartPage = initialDate?.page ?? DateTimeTimetable.today().page; - value = value.copyWith( + value = value.copyWithActivity( page: value.visibleRange.getTargetPageForFocus(rawStartPage), + activity: const IdleDateScrollActivity(), ); } @@ -46,9 +49,11 @@ class DateController extends ValueNotifier { VisibleDateRange get visibleRange => value.visibleRange; set visibleRange(VisibleDateRange visibleRange) { - value = value.copyWith( + cancelAnimation(); + value = value.copyWithActivity( page: visibleRange.getTargetPageForFocus(value.page), visibleRange: visibleRange, + activity: const IdleDateScrollActivity(), ); } @@ -91,16 +96,20 @@ class DateController extends ValueNotifier { Duration duration = const Duration(milliseconds: 200), required TickerProvider vsync, }) async { - _animationController?.dispose(); + cancelAnimation(); final controller = AnimationController(debugLabel: 'DateController', vsync: vsync); _animationController = controller; final previousPage = value.page; final targetPage = value.visibleRange.getTargetPageForFocus(page); + final targetDatePageValue = DatePageValue(visibleRange, targetPage); controller.addListener(() { - value = value.copyWith( + value = value.copyWithActivity( page: lerpDouble(previousPage, targetPage, controller.value)!, + activity: controller.isAnimating + ? DrivenDateScrollActivity(targetDatePageValue) + : const IdleDateScrollActivity(), ); }); @@ -120,8 +129,19 @@ class DateController extends ValueNotifier { } void jumpToPage(double page) { - value = - value.copyWith(page: value.visibleRange.getTargetPageForFocus(page)); + cancelAnimation(); + value = value.copyWithActivity( + page: value.visibleRange.getTargetPageForFocus(page), + activity: const IdleDateScrollActivity(), + ); + } + + void cancelAnimation() { + if (_animationController == null) return; + + value = value.copyWithActivity(activity: const IdleDateScrollActivity()); + _animationController!.dispose(); + _animationController = null; } bool _isDisposed = false; @@ -136,7 +156,7 @@ class DateController extends ValueNotifier { /// The value held by [DateController]. @immutable -class DatePageValue { +class DatePageValue with Diagnosticable { const DatePageValue(this.visibleRange, this.page); final VisibleDateRange visibleRange; @@ -191,8 +211,73 @@ class DatePageValue { } @override - String toString() => - 'DatePageValue(visibleRange = $visibleRange, page = $page)'; + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add( + DiagnosticsProperty('visibleRange', visibleRange), + ); + properties.add(DoubleProperty('page', page)); + properties.add(DateDiagnosticsProperty('date', date)); + } +} + +class DatePageValueWithScrollActivity extends DatePageValue { + const DatePageValueWithScrollActivity( + super.visibleRange, + super.page, + this.activity, + ); + + final DateScrollActivity activity; + + DatePageValueWithScrollActivity copyWithActivity({ + VisibleDateRange? visibleRange, + double? page, + required DateScrollActivity activity, + }) { + return DatePageValueWithScrollActivity( + visibleRange ?? this.visibleRange, + page ?? this.page, + activity, + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + .add(DiagnosticsProperty('activity', activity)); + } +} + +/// The equivalent of [ScrollActivity] for [DateController]. +@immutable +abstract class DateScrollActivity with Diagnosticable { + const DateScrollActivity(); +} + +/// A scroll activity that does nothing. +class IdleDateScrollActivity extends DateScrollActivity { + const IdleDateScrollActivity(); +} + +/// The activity a [DateController] performs when the user drags their finger +/// across the screen and is settling afterwards. +class DragDateScrollActivity extends DateScrollActivity { + const DragDateScrollActivity(); +} + +/// A scroll activity for when the [DateController] is animated to a new page. +class DrivenDateScrollActivity extends DateScrollActivity { + const DrivenDateScrollActivity(this.target); + + final DatePageValue target; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('target', target)); + } } /// Provides the [DateController] for Timetable widgets below it. @@ -202,10 +287,7 @@ class DatePageValue { /// * [TimetableConfig], which bundles multiple configuration widgets for /// Timetable. class DefaultDateController extends InheritedWidget { - const DefaultDateController({ - required this.controller, - required super.child, - }); + const DefaultDateController({required this.controller, required super.child}); final DateController controller; diff --git a/lib/src/date/date_page_view.dart b/lib/src/date/date_page_view.dart index 562e562..ce55d09 100644 --- a/lib/src/date/date_page_view.dart +++ b/lib/src/date/date_page_view.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -127,7 +128,7 @@ class _DatePageViewState extends State { double maxHeightFrom(int page) { return page .rangeTo(page + pageValue.visibleDayCount - 1) - .map((it) => _heights[it] ?? 0) + .map((it) => _heights[it] ?? 0) .max .toDouble(); } @@ -264,7 +265,17 @@ class MultiDateScrollPosition extends ScrollPositionWithSingleContext { _updateUserScrollDirectionFromDelta(newPixels - pixels); final overscroll = super.setPixels(newPixels); - controller.value = controller.value.copyWith(page: pixelsToPage(pixels)); + + final activity = this.activity; + final dateScrollActivity = activity is DragScrollActivity || + (activity is BallisticScrollActivity && + activity.velocity.abs() > precisionErrorTolerance) + ? const DragDateScrollActivity() + : const IdleDateScrollActivity(); + controller.value = controller.value.copyWithActivity( + page: pixelsToPage(pixels), + activity: dateScrollActivity, + ); return overscroll; } diff --git a/lib/src/date/month_page_view.dart b/lib/src/date/month_page_view.dart index 1acad44..e4c425d 100644 --- a/lib/src/date/month_page_view.dart +++ b/lib/src/date/month_page_view.dart @@ -94,9 +94,12 @@ class _MonthPageViewState extends State { final oldMaxHeight = _heights[page.floor()]; final newMaxHeight = _heights[page.ceil()]; - // When swiping, the next page might not have been measured yet. - if (oldMaxHeight == null) return newMaxHeight!; - if (newMaxHeight == null) return oldMaxHeight; + // When swiping, the next page might not have been measured yet. When + // jumping to a page that hasn't been measured yet, we might not have any + // heights for that or neighboring pages at all. + if (oldMaxHeight == null || newMaxHeight == null) { + return oldMaxHeight ?? newMaxHeight ?? _heights.values.min; + } return lerpDouble(oldMaxHeight, newMaxHeight, page - page.floorToDouble())!; } diff --git a/lib/src/date/visible_date_range.dart b/lib/src/date/visible_date_range.dart index e7640ba..b00cfaa 100644 --- a/lib/src/date/visible_date_range.dart +++ b/lib/src/date/visible_date_range.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/physics.dart'; import '../layouts/recurring_multi_date.dart'; @@ -5,7 +6,7 @@ import '../utils.dart'; /// Defines how many days are visible at once and whether they, e.g., snap to /// weeks. -abstract class VisibleDateRange { +abstract class VisibleDateRange with Diagnosticable { const VisibleDateRange({ required this.visibleDayCount, required this.canScroll, @@ -174,16 +175,27 @@ class DaysVisibleDateRange extends VisibleDateRange { ); return page - targetPage; } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(IntProperty('swipeRange', swipeRange)); + properties.add(DateDiagnosticsProperty('alignmentDate', alignmentDate)); + properties + .add(DateDiagnosticsProperty('minDate', minDate, defaultValue: null)); + properties.add(DoubleProperty('minPage', minPage, defaultValue: null)); + properties + .add(DateDiagnosticsProperty('maxDate', maxDate, defaultValue: null)); + properties.add(DoubleProperty('maxPage', maxPage, defaultValue: null)); + } } /// A non-scrollable [VisibleDateRange], used by [VisibleDateRange.fixed]. /// /// This is useful for, e.g., [RecurringMultiDateTimetable]. class FixedDaysVisibleDateRange extends VisibleDateRange { - FixedDaysVisibleDateRange( - this.startDate, - int visibleDayCount, - ) : assert(startDate.debugCheckIsValidTimetableDate()), + FixedDaysVisibleDateRange(this.startDate, int visibleDayCount) + : assert(startDate.debugCheckIsValidTimetableDate()), super(visibleDayCount: visibleDayCount, canScroll: false); final DateTime startDate; @@ -204,4 +216,11 @@ class FixedDaysVisibleDateRange extends VisibleDateRange { Tolerance tolerance = Tolerance.defaultTolerance, }) => page; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DateDiagnosticsProperty('startDate', startDate)); + properties.add(DoubleProperty('page', page)); + } } diff --git a/lib/src/event/all_day.dart b/lib/src/event/all_day.dart index 7862052..8542119 100644 --- a/lib/src/event/all_day.dart +++ b/lib/src/event/all_day.dart @@ -121,7 +121,7 @@ class AllDayEventBorder extends ShapeBorder { @override String toString() => - '${objectRuntimeType(this, 'RoundedRectangleBorder')}($side, $radii)'; + '${objectRuntimeType(this, 'AllDayEventBorder')}($side, $radii)'; } @immutable diff --git a/lib/src/time/controller.dart b/lib/src/time/controller.dart index 71157eb..e9b1224 100644 --- a/lib/src/time/controller.dart +++ b/lib/src/time/controller.dart @@ -191,7 +191,7 @@ class TimeController extends ValueNotifier { }) async { assert(_isValidRange(newValue)); - _animationController?.dispose(); + cancelAnimation(); final previousRange = value; _animationController = AnimationController(debugLabel: 'TimeController', vsync: vsync) @@ -205,7 +205,22 @@ class TimeController extends ValueNotifier { ..animateTo(1, duration: duration, curve: curve); } - void jumpToShowFullDay() => value = TimeRange.fullDay; + void jumpToShowFullDay() { + cancelAnimation(); + value = TimeRange.fullDay; + } + + void jumpTo(TimeRange range) { + assert(_isValidRange(range)); + + cancelAnimation(); + value = range; + } + + void cancelAnimation() { + _animationController?.dispose(); + _animationController = null; + } } /// Provides the [TimeController] for Timetable widgets below it. diff --git a/lib/src/utils.dart b/lib/src/utils.dart index ad9ab85..1e1e414 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,4 +1,5 @@ import 'package:dart_date/dart_date.dart' show Interval; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart' hide Interval; import 'week.dart'; @@ -162,6 +163,31 @@ extension DateTimeTimetable on DateTime { } } +class DateDiagnosticsProperty extends DiagnosticsProperty { + /// Create a diagnostics property for [Color]. + /// + /// The [showName], [style], and [level] arguments must not be null. + DateDiagnosticsProperty( + String super.name, + super.value, { + super.showName, + super.defaultValue, + super.style, + super.level, + }) : assert(value.isValidTimetableDate); + + @override + String valueToString({TextTreeConfiguration? parentConfiguration}) { + final value = this.value; + if (value == null) return value.toString(); + + final year = value.year.toString().padLeft(4, '0'); + final month = value.month.toString().padLeft(2, '0'); + final day = value.day.toString().padLeft(2, '0'); + return '$year-$month-$day'; + } +} + extension InternalDateTimeTimetable on DateTime { static DateTime create({ required int year, diff --git a/pubspec.yaml b/pubspec.yaml index 3ff332f..a46d77d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,16 @@ name: remind_timetable description: ReMinds timetable -version: 1.0.0-alpha.13 +version: 1.0.0-alpha.14 repository: https://github.com/Allistic/timetable environment: - sdk: '>=2.18.6 <3.0.0' - flutter: '>=2.13.0-0.4.pre' + sdk: '>=2.18.0 <3.0.0' + flutter: '>=3.3.0' + dependencies: async: ^2.6.0 - black_hole_flutter: ^0.3.0 + black_hole_flutter: ^1.0.0 collection: ^1.15.0 dart_date: ^1.1.0 dartx: ^1.0.0