memory leak fixes
This commit is contained in:
parent
5b73c8630b
commit
3909b9223d
15 changed files with 127 additions and 47 deletions
|
@ -4,6 +4,7 @@ import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/widgets/common/basic/popup/menu_row.dart';
|
import 'package:aves/widgets/common/basic/popup/menu_row.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
|
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
|
||||||
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:aves_video/aves_video.dart';
|
import 'package:aves_video/aves_video.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@ -25,9 +26,10 @@ class MuteToggler extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ValueListenableBuilder<bool>(
|
return NullableValueListenableBuilder<bool>(
|
||||||
valueListenable: controller?.canMuteNotifier ?? ValueNotifier(false),
|
valueListenable: controller?.canMuteNotifier,
|
||||||
builder: (context, canDo, child) {
|
builder: (context, value, child) {
|
||||||
|
final canDo = value ?? false;
|
||||||
return StreamBuilder<double>(
|
return StreamBuilder<double>(
|
||||||
stream: controller?.volumeStream ?? Stream.value(1.0),
|
stream: controller?.volumeStream ?? Stream.value(1.0),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
@ -66,9 +68,10 @@ class MuteTogglerCaption extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ValueListenableBuilder<bool>(
|
return NullableValueListenableBuilder<bool>(
|
||||||
valueListenable: controller?.canMuteNotifier ?? ValueNotifier(false),
|
valueListenable: controller?.canMuteNotifier,
|
||||||
builder: (context, canDo, child) {
|
builder: (context, value, child) {
|
||||||
|
final canDo = value ?? false;
|
||||||
return StreamBuilder<double>(
|
return StreamBuilder<double>(
|
||||||
stream: controller?.volumeStream ?? Stream.value(1.0),
|
stream: controller?.volumeStream ?? Stream.value(1.0),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/extensions/media_query.dart';
|
import 'package:aves/widgets/common/extensions/media_query.dart';
|
||||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||||
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
||||||
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -151,9 +152,10 @@ class SafeCutoutArea extends StatelessWidget {
|
||||||
return ValueListenableBuilder<EdgeInsets>(
|
return ValueListenableBuilder<EdgeInsets>(
|
||||||
valueListenable: AvesApp.cutoutInsetsNotifier,
|
valueListenable: AvesApp.cutoutInsetsNotifier,
|
||||||
builder: (context, cutoutInsets, child) {
|
builder: (context, cutoutInsets, child) {
|
||||||
return ValueListenableBuilder<double>(
|
return NullableValueListenableBuilder<double>(
|
||||||
valueListenable: animation ?? ValueNotifier(1),
|
valueListenable: animation,
|
||||||
builder: (context, factor, child) {
|
builder: (context, value, child) {
|
||||||
|
final double factor = value ?? 1.0;
|
||||||
final effectiveInsets = cutoutInsets * factor;
|
final effectiveInsets = cutoutInsets * factor;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: effectiveInsets,
|
padding: effectiveInsets,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/widgets/common/map/leaflet/latlng_tween.dart' as llt;
|
||||||
import 'package:aves/widgets/common/map/leaflet/scale_layer.dart';
|
import 'package:aves/widgets/common/map/leaflet/scale_layer.dart';
|
||||||
import 'package:aves/widgets/common/map/leaflet/tile_layers.dart';
|
import 'package:aves/widgets/common/map/leaflet/tile_layers.dart';
|
||||||
import 'package:aves_map/aves_map.dart';
|
import 'package:aves_map/aves_map.dart';
|
||||||
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
@ -172,8 +173,8 @@ class _EntryLeafletMapState<T> extends State<EntryLeafletMap<T>> with TickerProv
|
||||||
rotate: true,
|
rotate: true,
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
),
|
),
|
||||||
ValueListenableBuilder<LatLng?>(
|
NullableValueListenableBuilder<LatLng?>(
|
||||||
valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null),
|
valueListenable: widget.dotLocationNotifier,
|
||||||
builder: (context, dotLocation, child) => MarkerLayer(
|
builder: (context, dotLocation, child) => MarkerLayer(
|
||||||
markers: [
|
markers: [
|
||||||
if (dotLocation != null)
|
if (dotLocation != null)
|
||||||
|
@ -214,9 +215,10 @@ class _EntryLeafletMapState<T> extends State<EntryLeafletMap<T>> with TickerProv
|
||||||
final corner2 = overlayEntry.bottomRight;
|
final corner2 = overlayEntry.bottomRight;
|
||||||
if (corner1 == null || corner2 == null) return const SizedBox();
|
if (corner1 == null || corner2 == null) return const SizedBox();
|
||||||
|
|
||||||
return ValueListenableBuilder<double>(
|
return NullableValueListenableBuilder<double>(
|
||||||
valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1),
|
valueListenable: widget.overlayOpacityNotifier,
|
||||||
builder: (context, overlayOpacity, child) {
|
builder: (context, value, child) {
|
||||||
|
final double overlayOpacity = value ?? 1.0;
|
||||||
return OverlayImageLayer(
|
return OverlayImageLayer(
|
||||||
overlayImages: [
|
overlayImages: [
|
||||||
OverlayImage(
|
OverlayImage(
|
||||||
|
|
|
@ -24,7 +24,7 @@ class ViewerThumbnailPreview extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ViewerThumbnailPreviewState extends State<ViewerThumbnailPreview> {
|
class _ViewerThumbnailPreviewState extends State<ViewerThumbnailPreview> {
|
||||||
final ValueNotifier<int> _entryIndexNotifier = ValueNotifier(0);
|
late final ValueNotifier<int> _entryIndexNotifier;
|
||||||
final Debouncer _debouncer = Debouncer(delay: ADurations.viewerThumbnailScrollDebounceDelay);
|
final Debouncer _debouncer = Debouncer(delay: ADurations.viewerThumbnailScrollDebounceDelay);
|
||||||
|
|
||||||
List<AvesEntry> get entries => widget.entries;
|
List<AvesEntry> get entries => widget.entries;
|
||||||
|
@ -34,7 +34,7 @@ class _ViewerThumbnailPreviewState extends State<ViewerThumbnailPreview> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_entryIndexNotifier.value = widget.displayedIndex;
|
_entryIndexNotifier = ValueNotifier(widget.displayedIndex);
|
||||||
_entryIndexNotifier.addListener(_onScrollerIndexChanged);
|
_entryIndexNotifier.addListener(_onScrollerIndexChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import 'package:aves/widgets/viewer/action/entry_action_delegate.dart';
|
||||||
import 'package:aves/widgets/viewer/controls/notifications.dart';
|
import 'package:aves/widgets/viewer/controls/notifications.dart';
|
||||||
import 'package:aves/widgets/viewer/video/conductor.dart';
|
import 'package:aves/widgets/viewer/video/conductor.dart';
|
||||||
import 'package:aves_model/aves_model.dart';
|
import 'package:aves_model/aves_model.dart';
|
||||||
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:aves_video/aves_video.dart';
|
import 'package:aves_video/aves_video.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -438,15 +439,18 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
||||||
Widget? child;
|
Widget? child;
|
||||||
void onPressed() => actionDelegate.onActionSelected(context, action);
|
void onPressed() => actionDelegate.onActionSelected(context, action);
|
||||||
|
|
||||||
ValueListenableBuilder<bool> _buildFromListenable(ValueListenable<bool>? enabledNotifier) {
|
Widget _buildFromListenable(ValueListenable<bool>? enabledNotifier) {
|
||||||
return ValueListenableBuilder<bool>(
|
return NullableValueListenableBuilder<bool>(
|
||||||
valueListenable: enabledNotifier ?? ValueNotifier(false),
|
valueListenable: enabledNotifier,
|
||||||
builder: (context, canDo, child) => IconButton(
|
builder: (context, value, child) {
|
||||||
icon: child!,
|
final canDo = value ?? false;
|
||||||
onPressed: canDo ? onPressed : null,
|
return IconButton(
|
||||||
focusNode: focusNode,
|
icon: child!,
|
||||||
tooltip: action.getText(context),
|
onPressed: canDo ? onPressed : null,
|
||||||
),
|
focusNode: focusNode,
|
||||||
|
tooltip: action.getText(context),
|
||||||
|
);
|
||||||
|
},
|
||||||
child: action.getIcon(),
|
child: action.getIcon(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:aves/model/entry/entry.dart';
|
import 'package:aves/model/entry/entry.dart';
|
||||||
import 'package:aves/model/multipage.dart';
|
import 'package:aves/model/multipage.dart';
|
||||||
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
||||||
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class PageEntryBuilder extends StatelessWidget {
|
class PageEntryBuilder extends StatelessWidget {
|
||||||
|
@ -20,8 +21,8 @@ class PageEntryBuilder extends StatelessWidget {
|
||||||
stream: controller != null ? controller.infoStream : Stream.value(null),
|
stream: controller != null ? controller.infoStream : Stream.value(null),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final multiPageInfo = controller?.info;
|
final multiPageInfo = controller?.info;
|
||||||
return ValueListenableBuilder<int?>(
|
return NullableValueListenableBuilder<int?>(
|
||||||
valueListenable: controller?.pageNotifier ?? ValueNotifier(null),
|
valueListenable: controller?.pageNotifier,
|
||||||
builder: (context, page, child) {
|
builder: (context, page, child) {
|
||||||
final pageEntry = multiPageInfo?.getPageEntryByIndex(page);
|
final pageEntry = multiPageInfo?.getPageEntryByIndex(page);
|
||||||
return builder(pageEntry);
|
return builder(pageEntry);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:aves_map/aves_map.dart';
|
import 'package:aves_map/aves_map.dart';
|
||||||
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
import 'package:latlong2/latlong.dart' as ll;
|
import 'package:latlong2/latlong.dart' as ll;
|
||||||
|
@ -172,12 +173,13 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
|
||||||
|
|
||||||
final interactive = context.select<MapThemeData, bool>((v) => v.interactive);
|
final interactive = context.select<MapThemeData, bool>((v) => v.interactive);
|
||||||
final overlayEntry = widget.overlayEntry;
|
final overlayEntry = widget.overlayEntry;
|
||||||
return ValueListenableBuilder<ll.LatLng?>(
|
return NullableValueListenableBuilder<ll.LatLng?>(
|
||||||
valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null),
|
valueListenable: widget.dotLocationNotifier,
|
||||||
builder: (context, dotLocation, child) {
|
builder: (context, dotLocation, child) {
|
||||||
return ValueListenableBuilder<double>(
|
return NullableValueListenableBuilder<double>(
|
||||||
valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1),
|
valueListenable: widget.overlayOpacityNotifier,
|
||||||
builder: (context, overlayOpacity, child) {
|
builder: (context, value, child) {
|
||||||
|
final double overlayOpacity = value ?? 1.0;
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
_sizeNotifier.value = constraints.biggest;
|
_sizeNotifier.value = constraints.biggest;
|
||||||
|
|
|
@ -30,6 +30,13 @@ packages:
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
aves_utils:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "../aves_utils"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -12,6 +12,8 @@ dependencies:
|
||||||
path: ../aves_map
|
path: ../aves_map
|
||||||
aves_services:
|
aves_services:
|
||||||
path: ../aves_services
|
path: ../aves_services
|
||||||
|
aves_utils:
|
||||||
|
path: ../aves_utils
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
google_api_availability:
|
google_api_availability:
|
||||||
google_maps_flutter:
|
google_maps_flutter:
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:aves_map/aves_map.dart';
|
import 'package:aves_map/aves_map.dart';
|
||||||
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:huawei_map/huawei_map.dart';
|
import 'package:huawei_map/huawei_map.dart';
|
||||||
import 'package:latlong2/latlong.dart' as ll;
|
import 'package:latlong2/latlong.dart' as ll;
|
||||||
|
@ -146,12 +147,13 @@ class _EntryHmsMapState<T> extends State<EntryHmsMap<T>> {
|
||||||
|
|
||||||
final interactive = context.select<MapThemeData, bool>((v) => v.interactive);
|
final interactive = context.select<MapThemeData, bool>((v) => v.interactive);
|
||||||
// final overlayEntry = widget.overlayEntry;
|
// final overlayEntry = widget.overlayEntry;
|
||||||
return ValueListenableBuilder<ll.LatLng?>(
|
return NullableValueListenableBuilder<ll.LatLng?>(
|
||||||
valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null),
|
valueListenable: widget.dotLocationNotifier,
|
||||||
builder: (context, dotLocation, child) {
|
builder: (context, dotLocation, child) {
|
||||||
return ValueListenableBuilder<double>(
|
return NullableValueListenableBuilder<double>(
|
||||||
valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1),
|
valueListenable: widget.overlayOpacityNotifier,
|
||||||
builder: (context, overlayOpacity, child) {
|
builder: (context, value, child) {
|
||||||
|
// final double overlayOpacity = value ?? 1.0;
|
||||||
return HuaweiMap(
|
return HuaweiMap(
|
||||||
initialCameraPosition: CameraPosition(
|
initialCameraPosition: CameraPosition(
|
||||||
bearing: bounds.rotation,
|
bearing: bounds.rotation,
|
||||||
|
|
|
@ -37,6 +37,13 @@ packages:
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
aves_utils:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "../aves_utils"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -14,6 +14,8 @@ dependencies:
|
||||||
path: ../aves_platform_meta
|
path: ../aves_platform_meta
|
||||||
aves_services:
|
aves_services:
|
||||||
path: ../aves_services
|
path: ../aves_services
|
||||||
|
aves_utils:
|
||||||
|
path: ../aves_utils
|
||||||
# cf https://github.com/HMS-Core/hms-flutter-plugin/pull/296
|
# cf https://github.com/HMS-Core/hms-flutter-plugin/pull/296
|
||||||
huawei_hmsavailability:
|
huawei_hmsavailability:
|
||||||
git:
|
git:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
library aves_utils;
|
library aves_utils;
|
||||||
|
|
||||||
export 'src/change_notifier.dart';
|
|
||||||
export 'src/colors.dart';
|
export 'src/colors.dart';
|
||||||
|
export 'src/listenable.dart';
|
||||||
export 'src/optional_event_channel.dart';
|
export 'src/optional_event_channel.dart';
|
||||||
export 'src/vector_utils.dart';
|
export 'src/vector_utils.dart';
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
|
|
||||||
// `ChangeNotifier` wrapper to call `notify` without constraint
|
|
||||||
class AChangeNotifier extends ChangeNotifier {
|
|
||||||
void notify() {
|
|
||||||
// why is this protected?
|
|
||||||
super.notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
55
plugins/aves_utils/lib/src/listenable.dart
Normal file
55
plugins/aves_utils/lib/src/listenable.dart
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
// `ChangeNotifier` wrapper to call `notify` without constraint
|
||||||
|
class AChangeNotifier extends ChangeNotifier {
|
||||||
|
void notify() {
|
||||||
|
// why is this protected?
|
||||||
|
super.notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// contrary to standard `ValueListenableBuilder`, this widget allows providing a null listenable
|
||||||
|
class NullableValueListenableBuilder<T> extends StatefulWidget {
|
||||||
|
final ValueListenable<T?>? valueListenable;
|
||||||
|
final ValueWidgetBuilder<T?> builder;
|
||||||
|
final Widget? child;
|
||||||
|
|
||||||
|
const NullableValueListenableBuilder({
|
||||||
|
super.key,
|
||||||
|
required this.valueListenable,
|
||||||
|
required this.builder,
|
||||||
|
this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<NullableValueListenableBuilder> createState() => _NullableValueListenableBuilderState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NullableValueListenableBuilderState<T> extends State<NullableValueListenableBuilder<T>> {
|
||||||
|
ValueNotifier<T?>? _internalValueListenable;
|
||||||
|
|
||||||
|
ValueListenable<T?> get _valueListenable {
|
||||||
|
var listenable = widget.valueListenable;
|
||||||
|
if (listenable == null) {
|
||||||
|
_internalValueListenable ??= ValueNotifier(null);
|
||||||
|
listenable = _internalValueListenable;
|
||||||
|
}
|
||||||
|
return listenable!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_internalValueListenable?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ValueListenableBuilder<T?>(
|
||||||
|
valueListenable: _valueListenable,
|
||||||
|
builder: widget.builder,
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue