Skip to content

Commit

Permalink
Remix update
Browse files Browse the repository at this point in the history
  • Loading branch information
leoafarias committed Jul 17, 2024
1 parent 2daf51b commit 8f01f96
Show file tree
Hide file tree
Showing 20 changed files with 1,332 additions and 686 deletions.
13 changes: 4 additions & 9 deletions packages/mix/lib/src/core/deprecation_notices.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// ignore_for_file: camel_case_types

import '../attributes/enum/enum_util.dart';
import '../internal/widget_state/widget_state.dart';
import '../specs/image/image_spec.dart';
import '../variants/context_variant.dart';
import '../widgets/widgets.dart';
import '../variants/widget_state_variant.dart';
import 'attribute.dart';

class InternalMixDeprecatedAnnotation extends Deprecated {
Expand Down Expand Up @@ -50,15 +50,10 @@ typedef WidgetContextVariant = WidgetStateVariant;
updatedName: 'WidgetStateModel',
)
typedef PressableState = WidgetStateModel;
@RenamedDeprecated(
message: 'Use `WidgetStateAspect` instead.',
version: '2.0.0',
updatedName: 'WidgetStateAspect',
)
typedef PressableStateAspect = WidgetStateAspect;

@RenamedDeprecated(
message: 'Use `MixWidgetState` instead.',
version: '2.0.0',
updatedName: 'MixWidgetState',
)
typedef PressableCurrentState = MixWidgetState;
typedef PressableCurrentState = WidgetMixState;
17 changes: 10 additions & 7 deletions packages/mix/lib/src/core/styled_widget.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/widgets.dart';

import '../internal/widget_state/interactive_widget.dart';
import '../modifiers/modifiers.dart';
import 'core.dart';

Expand Down Expand Up @@ -45,16 +46,18 @@ abstract class StyledWidget extends StatelessWidget {
BuildContext context,
Widget Function(BuildContext context) builder,
) {
final inheritedMix = inherit ? Mix.maybeOfInherited(context) : null;
return InteractiveBuilder(builder: (context) {
final inheritedMix = inherit ? Mix.maybeOfInherited(context) : null;

final mix = style.of(context);
final mix = style.of(context);

final mergedMix = inheritedMix?.merge(mix) ?? mix;
final mergedMix = inheritedMix?.merge(mix) ?? mix;

return Mix(
data: mergedMix,
child: applyModifiers(mergedMix, Builder(builder: builder)),
);
return Mix(
data: mergedMix,
child: applyModifiers(mergedMix, Builder(builder: builder)),
);
});
}

Widget applyModifiers(MixData mix, Widget child) {
Expand Down
327 changes: 327 additions & 0 deletions packages/mix/lib/src/internal/widget_state/gesturable_builder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
import 'dart:async';

import 'package:flutter/material.dart';

class GesturableWidget extends GesturableWidgetBuilder {
const GesturableWidget({
super.key,
required super.child,
super.enableFeedback = false,
super.enabled = true,
super.onTap,
super.onLongPress,
super.onTapUp,
super.onTapCancel,
super.onLongPressStart,
super.onLongPressEnd,
super.onLongPressCancel,
super.onPanDown,
super.onPanUpdate,
super.onPanEnd,
super.excludeFromSemantics = false,
super.hitTestBehavior = HitTestBehavior.opaque,
required super.unpressDelay,
});

@override
State createState() => _GesturableState();
}

class _GesturableState extends GesturableWidgetStateBuilder<GesturableWidget> {}

abstract class GesturableWidgetBuilder extends StatefulWidget {
const GesturableWidgetBuilder({
super.key,
required this.enabled,
required this.child,
this.enableFeedback = false,
this.onTap,
this.onLongPress,
this.onTapUp,
this.onTapCancel,
this.onLongPressStart,
this.onLongPressEnd,
this.onLongPressCancel,
this.onPanDown,
this.onPanUpdate,
this.onPanEnd,
this.excludeFromSemantics = false,
this.hitTestBehavior = HitTestBehavior.opaque,
required this.unpressDelay,
});

final bool enabled;

/// The child widget.
final Widget child;

/// Whether to provide feedback for gestures.
final bool enableFeedback;

/// The callback that is called when the user stops touching the screen after a tap gesture.
final GestureTapUpCallback? onTapUp;

/// The callback that is called when the user cancels a tap gesture.
final GestureTapCancelCallback? onTapCancel;

/// The callback that is called when the user starts a long press gesture.
final GestureLongPressStartCallback? onLongPressStart;

/// The callback that is called when the user ends a long press gesture.
final GestureLongPressEndCallback? onLongPressEnd;

/// The callback that is called when the user cancels a long press gesture.
final GestureLongPressCallback? onLongPressCancel;

/// The callback that is called when the user starts a pan gesture.
final GestureDragDownCallback? onPanDown;

/// The callback that is called when the user moves their finger during a pan gesture.
final GestureDragUpdateCallback? onPanUpdate;

/// The callback that is called when the user ends a pan gesture.
final GestureDragEndCallback? onPanEnd;

/// The callback that is called when the widget is pressed.
final VoidCallback? onTap;

/// The callback that is called when the widget is long-pressed.
final VoidCallback? onLongPress;

/// Whether to exclude the widget from semantics.
final bool excludeFromSemantics;

/// How to behave during hit testing.
final HitTestBehavior hitTestBehavior;

/// The duration to wait after the press is released before updating the press state.
final Duration unpressDelay;
}

abstract class GesturableWidgetStateBuilder<T extends GesturableWidgetBuilder>
extends State<T> {
late final _GesturableDataController _controller;
int _pressCount = 0;
Timer? _timer;

@override
void initState() {
super.initState();
_controller = _GesturableDataController();
_controller.enabled = widget.enabled;
}

void _handlePanUpdate(DragUpdateDetails event) {
widget.onPanUpdate?.call(event);
}

void _handlePanDown(DragDownDetails details) {
widget.onPanDown?.call(details);
}

void _handlePanEnd(DragEndDetails details) {
_updatePress(true);
widget.onPanEnd?.call(details);
}

void _updatePress(bool isPressed) {
if (isPressed == _controller.pressed) return;

_controller.pressed = isPressed;

if (isPressed) {
_pressCount++;
final initialPressCount = _pressCount;
_unpressAfterDelay(initialPressCount);
}
}

void _onTapUp(TapUpDetails details) {
_controller.longPressed = false;
widget.onTapUp?.call(details);
}

void _onTapCancel() {
_controller.longPressed = false;
widget.onTapCancel?.call();
}

void _onLongPressStart(LongPressStartDetails details) {
_controller.longPressed = true;
widget.onLongPressStart?.call(details);
}

void _onLongPressEnd(LongPressEndDetails details) {
_controller.longPressed = false;
widget.onLongPressEnd?.call(details);
}

void _onLongPressCancel() {
_controller.longPressed = false;
widget.onLongPressCancel?.call();
}

void _unpressAfterDelay(int initialPressCount) {
void unpressCallback() {
if (_controller.pressed && _pressCount == initialPressCount) {
_controller.pressed = false;
}
}

_timer?.cancel();
_timer = null;

final delay = widget.unpressDelay;

if (delay != Duration.zero) {
_timer = Timer(delay, unpressCallback);
} else {
unpressCallback();
}
}

void _onTap() {
_updatePress(true);
widget.onTap?.call();
if (widget.enableFeedback) Feedback.forTap(context);
}

void _onLongPress() {
widget.onLongPress?.call();
if (widget.enableFeedback) Feedback.forLongPress(context);
}

@override
void didUpdateWidget(T oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.enabled != oldWidget.enabled) {
_controller.enabled = widget.enabled;
}
}

@override
void dispose() {
_timer?.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
return GestureDetector(
onTapUp: _onTapUp,
onTap: _onTap,
onTapCancel: _onTapCancel,
onLongPressCancel: _onLongPressCancel,
onLongPress: _onLongPress,
onLongPressStart: _onLongPressStart,
onLongPressEnd: _onLongPressEnd,
onPanDown: _handlePanDown,
onPanUpdate: _handlePanUpdate,
onPanEnd: _handlePanEnd,
behavior: widget.hitTestBehavior,
excludeFromSemantics: widget.excludeFromSemantics,
child: ListenableBuilder(
listenable: _controller,
builder: (context, _) {
return GesturableState(
longPressed: _controller.longPressed,
pressed: _controller.pressed,
child: widget.child,
);
},
),
);
}
}

class _GesturableDataController extends ChangeNotifier {
bool _enabled = false;
bool _pressed = false;
bool _longPressed = false;

bool get enabled => _enabled;
bool get disabled => !_enabled;
bool get pressed => enabled && _pressed;
bool get longPressed => enabled && _longPressed;

set enabled(bool value) {
if (_enabled == value) return;

_enabled = value;
notifyListeners();
}

set pressed(bool value) {
if (_pressed == value) return;

_pressed = value;
notifyListeners();
}

set longPressed(bool value) {
if (_longPressed == value) return;
_longPressed = value;
notifyListeners();
}
}

enum GestureStateAspect {
pressed,
longPressed,
}

class GesturableState extends InheritedModel<GestureStateAspect> {
const GesturableState({
super.key,
required super.child,
required this.longPressed,
required this.pressed,
});

static GesturableState of(
BuildContext context, [
GestureStateAspect? aspect,
]) {
final GesturableState? result = maybeOf(context, aspect);
assert(result != null, 'Unable to find an instance of GesturableState...');

return result!;
}

static GesturableState? maybeOf(
BuildContext context, [
GestureStateAspect? aspect,
]) {
return InheritedModel.inheritFrom<GesturableState>(
context,
aspect: aspect,
);
}

static bool pressedOf(BuildContext context) {
return of(context, GestureStateAspect.pressed).pressed;
}

static bool longPressedOf(BuildContext context) {
return of(context, GestureStateAspect.longPressed).longPressed;
}

final bool pressed;
final bool longPressed;
@override
bool updateShouldNotify(GesturableState oldWidget) {
return oldWidget.pressed != pressed || oldWidget.longPressed != longPressed;
}

@override
bool updateShouldNotifyDependent(
GesturableState oldWidget,
Set<GestureStateAspect> dependencies,
) {
return dependencies.contains(GestureStateAspect.pressed) &&
oldWidget.pressed != pressed ||
dependencies.contains(GestureStateAspect.longPressed) &&
oldWidget.longPressed != longPressed;
}
}
Loading

0 comments on commit 8f01f96

Please sign in to comment.