Skip to content

Commit

Permalink
Introduce YaruDraggable widget (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jupi007 authored May 30, 2022
1 parent 6d0e0ea commit 5b3cccf
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 3 deletions.
13 changes: 10 additions & 3 deletions example/lib/example_page_items.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:yaru_widgets_example/pages/carousel_page.dart';
import 'package:yaru_widgets_example/pages/check_box_row_page.dart';
import 'package:yaru_widgets_example/pages/color_disk_page.dart';
import 'package:yaru_widgets_example/pages/color_picker_page.dart';
import 'package:yaru_widgets_example/pages/draggable_page.dart';
import 'package:yaru_widgets_example/pages/extra_option_row_page.dart';
import 'package:yaru_widgets_example/pages/round_toggle_button_page.dart';
import 'package:yaru_widgets_example/pages/section_page.dart';
Expand Down Expand Up @@ -175,7 +176,13 @@ final examplePageItems = <YaruPageItem>[
),
),
YaruPageItem(
titleBuilder: (context) => Text('YaruRoundToggleButton'),
builder: (context) => RoundToggleButtonPage(),
iconData: YaruIcons.app_grid)
titleBuilder: (context) => Text('YaruRoundToggleButton'),
builder: (context) => RoundToggleButtonPage(),
iconData: YaruIcons.app_grid,
),
YaruPageItem(
titleBuilder: (context) => Text('YaruDraggable'),
builder: (context) => DraggablePage(),
iconData: YaruIcons.drag_handle,
),
];
62 changes: 62 additions & 0 deletions example/lib/pages/draggable_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:yaru_widgets/yaru_widgets.dart';

class DraggablePage extends StatelessWidget {
const DraggablePage({super.key});

@override
Widget build(BuildContext context) {
return YaruPage(children: [
Container(
color: Theme.of(context).colorScheme.onSurface.withOpacity(.1),
child: SizedBox(
width: 500,
height: 250,
child: Stack(
children: [
YaruDraggable(
initialPosition: Offset(0, 0),
onDragUpdate: (currentPosition, nextPosition) {
double dx = nextPosition.dx;
double dy = nextPosition.dy;

if (dx < 0) dx = 0;
if (dy < 0) dy = 0;
if (dx > 500 - 192) dx = 500 - 192;
if (dy > 250 - 108) dy = 250 - 108;

return Offset(dx, dy);
},
childBuilder: (context, position, isDragging, isHovering) =>
SizedBox(
width: 192,
height: 108,
child: AnimatedOpacity(
opacity: isDragging ? 1 : .85,
duration: Duration(milliseconds: 100),
child: Container(
decoration: BoxDecoration(
border: Border(top: BorderSide(width: 10)),
color: Theme.of(context).primaryColor,
),
child: Center(
child: Text(
position.toString(),
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
),
),
),
cursor: SystemMouseCursors.grab,
dragCursor: SystemMouseCursors.grabbing,
),
],
),
),
)
]);
}
}
114 changes: 114 additions & 0 deletions lib/src/yaru_draggable.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import 'package:flutter/material.dart';

/// A draggable positioned widget - have to be child of a [Stack]
class YaruDraggable extends StatefulWidget {
const YaruDraggable({
super.key,
required this.childBuilder,
required this.initialPosition,
this.onDragStart,
this.onDragUpdate,
this.onDragEnd,
this.cursor,
this.dragCursor,
});

/// Initial position of this element
final Offset initialPosition;

/// Builder callback of the child widget
final Widget Function(
BuildContext context,
Offset position,
bool isDragging,
bool _isHovering,
) childBuilder;

/// Callback called when this element starts to be dragged
final void Function()? onDragStart;

/// Callback called on each position update - usefull for collision checks
///
/// It takes the current position and the candidate new one and should return the real next position.
/// If null, the candidate next position is simply used.
final Offset Function(
Offset currentPosition,
Offset nextPosition,
)? onDragUpdate;

/// Callback called when this element finished to be dragged
final void Function()? onDragEnd;

/// Cursor used when hovering this element, also used when dragging if [dragCursor] is null
final MouseCursor? cursor;

/// Cursor used when dragging this element
final MouseCursor? dragCursor;

@override
_YaruDraggableState createState() => _YaruDraggableState();
}

class _YaruDraggableState extends State<YaruDraggable> {
late Offset _position = widget.initialPosition;
Offset _initialPosition = const Offset(0, 0);
Offset _moveOffset = const Offset(0, 0);

bool _isDragging = false;
bool _isHovering = false;

@override
Widget build(BuildContext context) {
return Positioned(
left: _position.dx,
top: _position.dy,
child: MouseRegion(
cursor: _getCursor(),
onEnter: (event) => setState(() {
_isHovering = true;
}),
onExit: (event) => setState(() {
_isHovering = false;
}),
child: GestureDetector(
onPanStart: (details) => setState(() {
_isDragging = true;
_initialPosition = _position;
if (widget.onDragStart != null) widget.onDragStart!();
}),
onPanUpdate: (details) => setState(() {
_moveOffset += details.delta;

final double dx = _initialPosition.dx + _moveOffset.dx;
final double dy = _initialPosition.dy + _moveOffset.dy;

_position = widget.onDragUpdate != null
? widget.onDragUpdate!(_position, Offset(dx, dy))
: Offset(dx, dy);
}),
onPanEnd: (details) => setState(() {
_isDragging = false;
_moveOffset = const Offset(0, 0);
if (widget.onDragEnd != null) widget.onDragEnd!();
}),
child: Builder(builder: (context) {
return widget.childBuilder(
context, _position, _isDragging, _isHovering);
}),
),
),
);
}

MouseCursor _getCursor() {
if (widget.cursor == null) {
return MouseCursor.defer;
}

if (widget.dragCursor != null && _isDragging) {
return widget.dragCursor!;
}

return widget.cursor!;
}
}
1 change: 1 addition & 0 deletions lib/yaru_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export 'src/yaru_color_disk.dart';
export 'src/yaru_color_picker_button.dart';
export 'src/yaru_compact_layout.dart';
export 'src/yaru_dialog_title.dart';
export 'src/yaru_draggable.dart';
export 'src/yaru_expandable.dart';
export 'src/yaru_expansion_panel_list.dart';
export 'src/yaru_extra_option_row.dart';
Expand Down

0 comments on commit 5b3cccf

Please sign in to comment.