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/extensions/build_context.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:flutter/material.dart';
|
||||
|
||||
|
@ -25,9 +26,10 @@ class MuteToggler extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: controller?.canMuteNotifier ?? ValueNotifier(false),
|
||||
builder: (context, canDo, child) {
|
||||
return NullableValueListenableBuilder<bool>(
|
||||
valueListenable: controller?.canMuteNotifier,
|
||||
builder: (context, value, child) {
|
||||
final canDo = value ?? false;
|
||||
return StreamBuilder<double>(
|
||||
stream: controller?.volumeStream ?? Stream.value(1.0),
|
||||
builder: (context, snapshot) {
|
||||
|
@ -66,9 +68,10 @@ class MuteTogglerCaption extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: controller?.canMuteNotifier ?? ValueNotifier(false),
|
||||
builder: (context, canDo, child) {
|
||||
return NullableValueListenableBuilder<bool>(
|
||||
valueListenable: controller?.canMuteNotifier,
|
||||
builder: (context, value, child) {
|
||||
final canDo = value ?? false;
|
||||
return StreamBuilder<double>(
|
||||
stream: controller?.volumeStream ?? Stream.value(1.0),
|
||||
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/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -151,9 +152,10 @@ class SafeCutoutArea extends StatelessWidget {
|
|||
return ValueListenableBuilder<EdgeInsets>(
|
||||
valueListenable: AvesApp.cutoutInsetsNotifier,
|
||||
builder: (context, cutoutInsets, child) {
|
||||
return ValueListenableBuilder<double>(
|
||||
valueListenable: animation ?? ValueNotifier(1),
|
||||
builder: (context, factor, child) {
|
||||
return NullableValueListenableBuilder<double>(
|
||||
valueListenable: animation,
|
||||
builder: (context, value, child) {
|
||||
final double factor = value ?? 1.0;
|
||||
final effectiveInsets = cutoutInsets * factor;
|
||||
return Padding(
|
||||
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/tile_layers.dart';
|
||||
import 'package:aves_map/aves_map.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
@ -172,8 +173,8 @@ class _EntryLeafletMapState<T> extends State<EntryLeafletMap<T>> with TickerProv
|
|||
rotate: true,
|
||||
alignment: Alignment.bottomCenter,
|
||||
),
|
||||
ValueListenableBuilder<LatLng?>(
|
||||
valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null),
|
||||
NullableValueListenableBuilder<LatLng?>(
|
||||
valueListenable: widget.dotLocationNotifier,
|
||||
builder: (context, dotLocation, child) => MarkerLayer(
|
||||
markers: [
|
||||
if (dotLocation != null)
|
||||
|
@ -214,9 +215,10 @@ class _EntryLeafletMapState<T> extends State<EntryLeafletMap<T>> with TickerProv
|
|||
final corner2 = overlayEntry.bottomRight;
|
||||
if (corner1 == null || corner2 == null) return const SizedBox();
|
||||
|
||||
return ValueListenableBuilder<double>(
|
||||
valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1),
|
||||
builder: (context, overlayOpacity, child) {
|
||||
return NullableValueListenableBuilder<double>(
|
||||
valueListenable: widget.overlayOpacityNotifier,
|
||||
builder: (context, value, child) {
|
||||
final double overlayOpacity = value ?? 1.0;
|
||||
return OverlayImageLayer(
|
||||
overlayImages: [
|
||||
OverlayImage(
|
||||
|
|
|
@ -24,7 +24,7 @@ class ViewerThumbnailPreview extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ViewerThumbnailPreviewState extends State<ViewerThumbnailPreview> {
|
||||
final ValueNotifier<int> _entryIndexNotifier = ValueNotifier(0);
|
||||
late final ValueNotifier<int> _entryIndexNotifier;
|
||||
final Debouncer _debouncer = Debouncer(delay: ADurations.viewerThumbnailScrollDebounceDelay);
|
||||
|
||||
List<AvesEntry> get entries => widget.entries;
|
||||
|
@ -34,7 +34,7 @@ class _ViewerThumbnailPreviewState extends State<ViewerThumbnailPreview> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_entryIndexNotifier.value = widget.displayedIndex;
|
||||
_entryIndexNotifier = ValueNotifier(widget.displayedIndex);
|
||||
_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/video/conductor.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:aves_video/aves_video.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -438,15 +439,18 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
|||
Widget? child;
|
||||
void onPressed() => actionDelegate.onActionSelected(context, action);
|
||||
|
||||
ValueListenableBuilder<bool> _buildFromListenable(ValueListenable<bool>? enabledNotifier) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: enabledNotifier ?? ValueNotifier(false),
|
||||
builder: (context, canDo, child) => IconButton(
|
||||
Widget _buildFromListenable(ValueListenable<bool>? enabledNotifier) {
|
||||
return NullableValueListenableBuilder<bool>(
|
||||
valueListenable: enabledNotifier,
|
||||
builder: (context, value, child) {
|
||||
final canDo = value ?? false;
|
||||
return IconButton(
|
||||
icon: child!,
|
||||
onPressed: canDo ? onPressed : null,
|
||||
focusNode: focusNode,
|
||||
tooltip: action.getText(context),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: action.getIcon(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/multipage.dart';
|
||||
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class PageEntryBuilder extends StatelessWidget {
|
||||
|
@ -20,8 +21,8 @@ class PageEntryBuilder extends StatelessWidget {
|
|||
stream: controller != null ? controller.infoStream : Stream.value(null),
|
||||
builder: (context, snapshot) {
|
||||
final multiPageInfo = controller?.info;
|
||||
return ValueListenableBuilder<int?>(
|
||||
valueListenable: controller?.pageNotifier ?? ValueNotifier(null),
|
||||
return NullableValueListenableBuilder<int?>(
|
||||
valueListenable: controller?.pageNotifier,
|
||||
builder: (context, page, child) {
|
||||
final pageEntry = multiPageInfo?.getPageEntryByIndex(page);
|
||||
return builder(pageEntry);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:aves_map/aves_map.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
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 overlayEntry = widget.overlayEntry;
|
||||
return ValueListenableBuilder<ll.LatLng?>(
|
||||
valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null),
|
||||
return NullableValueListenableBuilder<ll.LatLng?>(
|
||||
valueListenable: widget.dotLocationNotifier,
|
||||
builder: (context, dotLocation, child) {
|
||||
return ValueListenableBuilder<double>(
|
||||
valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1),
|
||||
builder: (context, overlayOpacity, child) {
|
||||
return NullableValueListenableBuilder<double>(
|
||||
valueListenable: widget.overlayOpacityNotifier,
|
||||
builder: (context, value, child) {
|
||||
final double overlayOpacity = value ?? 1.0;
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
_sizeNotifier.value = constraints.biggest;
|
||||
|
|
|
@ -30,6 +30,13 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
aves_utils:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../aves_utils"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -12,6 +12,8 @@ dependencies:
|
|||
path: ../aves_map
|
||||
aves_services:
|
||||
path: ../aves_services
|
||||
aves_utils:
|
||||
path: ../aves_utils
|
||||
device_info_plus:
|
||||
google_api_availability:
|
||||
google_maps_flutter:
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:aves_map/aves_map.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:huawei_map/huawei_map.dart';
|
||||
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 overlayEntry = widget.overlayEntry;
|
||||
return ValueListenableBuilder<ll.LatLng?>(
|
||||
valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null),
|
||||
return NullableValueListenableBuilder<ll.LatLng?>(
|
||||
valueListenable: widget.dotLocationNotifier,
|
||||
builder: (context, dotLocation, child) {
|
||||
return ValueListenableBuilder<double>(
|
||||
valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1),
|
||||
builder: (context, overlayOpacity, child) {
|
||||
return NullableValueListenableBuilder<double>(
|
||||
valueListenable: widget.overlayOpacityNotifier,
|
||||
builder: (context, value, child) {
|
||||
// final double overlayOpacity = value ?? 1.0;
|
||||
return HuaweiMap(
|
||||
initialCameraPosition: CameraPosition(
|
||||
bearing: bounds.rotation,
|
||||
|
|
|
@ -37,6 +37,13 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
aves_utils:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../aves_utils"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -14,6 +14,8 @@ dependencies:
|
|||
path: ../aves_platform_meta
|
||||
aves_services:
|
||||
path: ../aves_services
|
||||
aves_utils:
|
||||
path: ../aves_utils
|
||||
# cf https://github.com/HMS-Core/hms-flutter-plugin/pull/296
|
||||
huawei_hmsavailability:
|
||||
git:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
library aves_utils;
|
||||
|
||||
export 'src/change_notifier.dart';
|
||||
export 'src/colors.dart';
|
||||
export 'src/listenable.dart';
|
||||
export 'src/optional_event_channel.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