Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 4.0.5
* **FEAT:** https://github.com/feduke-nukem/flutter_easy_dialogs/issues/40

## 4.0.4
* **FIX:** Fixed https://github.com/feduke-nukem/flutter_easy_dialogs/issues/37
* **FIX:** Fixed https://github.com/feduke-nukem/flutter_easy_dialogs/issues/38
Expand Down
2 changes: 2 additions & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
<string>13.0</string>
</dict>
</plist>
6 changes: 3 additions & 3 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -352,7 +352,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -401,7 +401,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
Expand All @@ -43,11 +44,13 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UIKit
import Flutter

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
55 changes: 41 additions & 14 deletions example/lib/positioned/screens/positioned_dialog_basic_usage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class _PositionedDialogManagerBasicUsageScreenState
_dismissibleTap: EasyDialogDismiss.tap(
onDismissed: () => _count++,
willDismiss: () => true,
behavior: HitTestBehavior.deferToChild,
),
_dismissibleHorizontalSwipe: EasyDialogDismiss.swipe(
onDismissed: () => _count++,
Expand All @@ -53,6 +54,7 @@ class _PositionedDialogManagerBasicUsageScreenState
_dismissibleAnimatedTap: EasyDialogDismiss.animatedTap(
onDismissed: () => _count++,
willDismiss: () => true,
behavior: HitTestBehavior.translucent,
),
};
final _animatorsDropDownItems = _animations.entries
Expand Down Expand Up @@ -88,6 +90,7 @@ class _PositionedDialogManagerBasicUsageScreenState
EasyDialogPosition _selectedPosition = EasyDialogPosition.top;
late var _selectedDismissible = _dismissibles.values.first;
var _isAutoHide = false;
var _isDraggable = false;
var _autoHideDuration = 300.0;

@override
Expand Down Expand Up @@ -158,6 +161,15 @@ class _PositionedDialogManagerBasicUsageScreenState
onChanged: (value) => setState(() => _autoHideDuration = value),
),
],
CheckboxListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 20.0,
vertical: 5.0,
),
title: const Text('Draggable'),
value: _isDraggable,
onChanged: (value) => setState(() => _isDraggable = value!),
),
ElevatedButton(
onPressed: _show,
child: const Text('Show'),
Expand All @@ -182,30 +194,45 @@ class _PositionedDialogManagerBasicUsageScreenState
Future<void> _show() async {
final messenger = ScaffoldMessenger.of(context);

final content = Container(
height: 150.0,
color: Colors.blue[900],
alignment: Alignment.center,
child: Text(
_selectedPosition.name,
style: const TextStyle(
color: Colors.white,
fontSize: 30.0,
final content = ColoredBox(
color: Colors.blue[900]!,
child: SizedBox(
height: 150.0,
width: 50,
child: Text(
_selectedPosition.name,
style: const TextStyle(
color: Colors.white,
fontSize: 30.0,
),
),
),
);

final result = await content
var dialog = content
.positioned(
position: _selectedPosition,
autoHideDuration: _isAutoHide
? Duration(milliseconds: _autoHideDuration.toInt())
: null,
)
.decorate(const PositionedShell.banner())
.decorate(_selectedAnimation)
.decorate(_selectedDismissible)
.show();
.decorate(const PositionedShell.banner());

if (_isDraggable) {
final screenSize = MediaQuery.sizeOf(context);
dialog = dialog.draggable(
bounds: Rect.fromLTWH(
0,
0,
screenSize.width,
screenSize.height,
),
);
}

dialog = dialog.decorate(_selectedAnimation).decorate(_selectedDismissible);

final result = await dialog.show();

if (result == null) return;
messenger
Expand Down
10 changes: 5 additions & 5 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
path:
dependency: transitive
description:
Expand Down Expand Up @@ -195,10 +195,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
source: hosted
version: "0.7.6"
version: "0.7.7"
vector_math:
dependency: transitive
description:
Expand All @@ -216,5 +216,5 @@ packages:
source: hosted
version: "15.0.0"
sdks:
dart: ">=3.7.0-0 <4.0.0"
dart: ">=3.8.0-0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
7 changes: 5 additions & 2 deletions lib/src/core/easy_dialogs_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -760,11 +760,14 @@ extension EasyDialogsX on EasyDialog {
);
}

EasyDialog draggable() {
EasyDialog draggable({
Rect? bounds,
}) {
return decorate(
EasyDialogDecoration.builder(
(context, dialog) => FreePositioned(
(_, dialog) => FreePositioned(
child: dialog.content,
bounds: bounds,
),
),
);
Expand Down
122 changes: 91 additions & 31 deletions lib/src/core/widget/free_positioned.dart
Original file line number Diff line number Diff line change
@@ -1,59 +1,119 @@
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easy_dialogs/src/positioned/positioned.dart'
show PositionedDialog;

class FreePositioned extends StatefulWidget {
final Widget child;
final Rect? bounds;

const FreePositioned({required this.child});
const FreePositioned({
required this.child,
this.bounds,
super.key,
});

@override
State<FreePositioned> createState() => _FreePositionedState();
}

class _FreePositionedState extends State<FreePositioned> {
var _x = 0.0;
var _y = 0.0;
AlignmentGeometry? _alignment;
var _offset = Offset(0.0, 0.0);
Offset? _globalOffset;

@override
Widget build(BuildContext context) {
final screenSize = MediaQuery.sizeOf(context);

final result = Builder(
builder: (context) => GestureDetector(
child: widget.child,
onPanStart: (details) => _onPanStart(details, context),
onPanUpdate: (details) => _onPanUpdate(details, context),
),
);

final alignment = PositionedDialog.maybeOf(context)?.alignment;

return Stack(
children: [
Positioned(
left: _x,
top: _y,
child: GestureDetector(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: screenSize.width,
maxHeight: screenSize.height,
),
// Not the best approach to apply alignment but it works for now,
// need to be reworked in the future
child: _alignment == null
? widget.child
: Align(
alignment: _alignment!,
child: widget.child,
),
),
onPanUpdate: (details) => setState(
() {
_x += details.delta.dx;
_y += details.delta.dy;
},
left: _offset.dx,
top: _offset.dy,
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: screenSize.width,
maxHeight: screenSize.height,
),
child: alignment == null
? result
: Align(
alignment: alignment,
child: result,
),
),
),
],
);
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
void _onPanStart(DragStartDetails details, BuildContext context) {
final renderBox = context.findRenderObject() as RenderBox?;
if (renderBox == null) return;

_globalOffset = renderBox.localToGlobal(Offset.zero);
}

void _onPanUpdate(DragUpdateDetails details, BuildContext context) {
final globalOffset = _globalOffset;

if (globalOffset == null) {
return;
}

final deltaX = details.delta.dx;
final deltaY = details.delta.dy;
final newGlobalX = globalOffset.dx + deltaX;
final newGlobalY = globalOffset.dy + deltaY;
final oldOffset = _offset;

final bounds = widget.bounds;

if (bounds == null) {
setState(() {
_offset = Offset(_offset.dx + deltaX, _offset.dy + deltaY);
_globalOffset = Offset(newGlobalX, newGlobalY);
});

return;
}

final RenderBox? renderBox = context.findRenderObject() as RenderBox?;

if (renderBox == null) {
return;
}

final childSize = renderBox.size;

final inBoundsX = newGlobalX >= bounds.left &&
newGlobalX + childSize.width <= bounds.right;
final inBoundsY = newGlobalY >= bounds.top &&
newGlobalY + childSize.height <= bounds.bottom;

if (inBoundsX) {
_offset = Offset(_offset.dx + deltaX, _offset.dy);
_globalOffset = Offset(newGlobalX, newGlobalY);
}

if (inBoundsY) {
_offset = Offset(_offset.dx, _offset.dy + deltaY);
_globalOffset = Offset(newGlobalX, newGlobalY);
}

if (oldOffset == _offset) {
return;
}

_alignment = context.findAncestorWidgetOfExactType<Align>()?.alignment;
setState(() {});
}
}
Loading