Skip to content

Commit 838127c

Browse files
committed
Add DropRegion and DragOutArea with an example
1 parent b17f77e commit 838127c

67 files changed

Lines changed: 3709 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.build/
9+
.buildlog/
10+
.history
11+
.svn/
12+
.swiftpm/
13+
migrate_working_dir/
14+
15+
# IntelliJ related
16+
*.iml
17+
*.ipr
18+
*.iws
19+
.idea/
20+
21+
# The .vscode folder contains launch configuration and tasks you configure in
22+
# VS Code which you may wish to be included in version control, so this line
23+
# is commented out by default.
24+
#.vscode/
25+
26+
# Flutter/Dart/Pub related
27+
**/doc/api/
28+
**/ios/Flutter/.last_build_id
29+
.dart_tool/
30+
.flutter-plugins-dependencies
31+
.pub-cache/
32+
.pub/
33+
/build/
34+
/coverage/
35+
36+
# Symbolication related
37+
app.*.symbols
38+
39+
# Obfuscation related
40+
app.*.map.json
41+
42+
# Android Studio will place build artifacts here
43+
/android/app/debug
44+
/android/app/profile
45+
/android/app/release
46+
47+
# Widget Preview related
48+
.widget_preview/
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: "c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0"
8+
channel: "main"
9+
10+
project_type: app
11+
12+
# Tracks metadata for the flutter migrate command
13+
migration:
14+
platforms:
15+
- platform: root
16+
create_revision: c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0
17+
base_revision: c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0
18+
- platform: linux
19+
create_revision: c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0
20+
base_revision: c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0
21+
- platform: macos
22+
create_revision: c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0
23+
base_revision: c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0
24+
- platform: windows
25+
create_revision: c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0
26+
base_revision: c8c5e3bd7542962ee8b09a12ac469d7d1ff823e0
27+
28+
# User provided section
29+
30+
# List of Local paths (relative to this file) that should be
31+
# ignored by the migrate tool.
32+
#
33+
# Files that are not part of the templates will be ignored by default.
34+
unmanaged_files:
35+
- 'lib/main.dart'
36+
- 'ios/Runner.xcodeproj/project.pbxproj'
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# drag_drop_example
2+
3+
Drag and drop between the window and other applications, with the two drag and
4+
drop widgets of `nativeapi`:
5+
6+
- `DropRegion` — the left panel. Drop files (from Finder / Explorer / a file
7+
manager) or text (from an editor) on it; it highlights while a drag is over it
8+
and lists what was dropped.
9+
- `DragOutArea` — the two cards on the right. Drag the note into a file manager
10+
to copy it there, or the text into an editor. Both can also be dropped on the
11+
left panel. "Last drag" shows what the target did (`copy`, or `none` when
12+
nothing was dropped).
13+
14+
```bash
15+
flutter run -d macos # or windows, linux
16+
```
17+
18+
The real-desktop test of this example lives in the workspace repository:
19+
`tools/gui/flutter_drag_drop_test.py` (macOS). It finds the panels by their
20+
texts, so keep those in `lib/main.dart` in sync with it.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This file configures the analyzer, which statically analyzes Dart code to
2+
# check for errors, warnings, and lints.
3+
#
4+
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5+
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6+
# invoked from the command line by running `flutter analyze`.
7+
8+
# The following line activates a set of recommended lints for Flutter apps,
9+
# packages, and plugins designed to encourage good coding practices.
10+
analyzer:
11+
exclude:
12+
- build/**
13+
- windows/**
14+
- macos/**
15+
- linux/**
16+
include: package:flutter_lints/flutter.yaml
17+
18+
linter:
19+
# The lint rules applied to this project can be customized in the
20+
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
21+
# included above or to enable additional rules. A list of all available lints
22+
# and their documentation is published at https://dart.dev/lints.
23+
#
24+
# Instead of disabling a lint rule for the entire project in the
25+
# section below, it can also be suppressed for a single line of code
26+
# or a specific dart file by using the `// ignore: name_of_lint` and
27+
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
28+
# producing the lint.
29+
rules:
30+
# avoid_print: false # Uncomment to disable the `avoid_print` rule
31+
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
32+
33+
# Additional information about this file can be found at
34+
# https://dart.dev/guides/language/analysis-options
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter/material.dart' hide Image;
4+
import 'package:nativeapi/nativeapi.dart';
5+
6+
/// Drag and drop with the two widgets of `nativeapi`:
7+
///
8+
/// - the left panel is a [DropRegion]: drop files or text on it;
9+
/// - the cards on the right are [DragOutArea]s: drag them into a file manager,
10+
/// an editor, or onto the left panel.
11+
///
12+
/// tools/gui/flutter_drag_drop_test.* in the workspace repository drives this
13+
/// example by its texts; keep them in sync.
14+
void main() {
15+
runApp(const DragDropApp());
16+
}
17+
18+
class DragDropApp extends StatelessWidget {
19+
const DragDropApp({super.key});
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
return MaterialApp(
24+
debugShowCheckedModeBanner: false,
25+
theme: ThemeData(colorSchemeSeed: Colors.teal, useMaterial3: true),
26+
home: const DragDropPage(),
27+
);
28+
}
29+
}
30+
31+
class DragDropPage extends StatefulWidget {
32+
const DragDropPage({super.key});
33+
34+
@override
35+
State<DragDropPage> createState() => _DragDropPageState();
36+
}
37+
38+
class _DragDropPageState extends State<DragDropPage> {
39+
late final File _file = _createFile();
40+
bool _hovering = false;
41+
Offset? _hoverPosition;
42+
int _drops = 0;
43+
List<String> _droppedFiles = const [];
44+
String? _droppedText;
45+
String _lastDragResult = '-';
46+
47+
static File _createFile() {
48+
final file = File(
49+
'${Directory.systemTemp.path}${Platform.pathSeparator}'
50+
'nativeapi-drag-drop-note.txt',
51+
);
52+
file.writeAsStringSync('A note dragged out of drag_drop_example.\n');
53+
return file;
54+
}
55+
56+
@override
57+
void initState() {
58+
super.initState();
59+
final window = WindowManager.instance.getCurrent();
60+
if (window == null) return;
61+
window.title = 'nativeapi · Drag and drop';
62+
window.contentSize = const Size(760, 460);
63+
window.center();
64+
}
65+
66+
@override
67+
Widget build(BuildContext context) {
68+
final theme = Theme.of(context);
69+
return Scaffold(
70+
body: Padding(
71+
padding: const EdgeInsets.all(16),
72+
child: Row(
73+
crossAxisAlignment: CrossAxisAlignment.stretch,
74+
children: [
75+
Expanded(child: _buildDropPanel(theme)),
76+
const SizedBox(width: 16),
77+
SizedBox(width: 260, child: _buildDragPanel(theme)),
78+
],
79+
),
80+
),
81+
);
82+
}
83+
84+
Widget _buildDropPanel(ThemeData theme) {
85+
final colors = theme.colorScheme;
86+
return DropRegion(
87+
onDragEntered: (position) => setState(() {
88+
_hovering = true;
89+
_hoverPosition = position;
90+
}),
91+
onDragUpdated: (position) => setState(() => _hoverPosition = position),
92+
onDragExited: () => setState(() {
93+
_hovering = false;
94+
_hoverPosition = null;
95+
}),
96+
onDropped: (details) => setState(() {
97+
_hovering = false;
98+
_hoverPosition = null;
99+
_drops++;
100+
_droppedFiles = details.filePaths;
101+
_droppedText = details.text;
102+
}),
103+
child: AnimatedContainer(
104+
duration: const Duration(milliseconds: 120),
105+
decoration: BoxDecoration(
106+
color: _hovering
107+
? colors.primaryContainer
108+
: colors.surfaceContainerHighest,
109+
borderRadius: BorderRadius.circular(12),
110+
border: Border.all(
111+
color: _hovering ? colors.primary : colors.outlineVariant,
112+
width: 2,
113+
),
114+
),
115+
padding: const EdgeInsets.all(16),
116+
child: Column(
117+
crossAxisAlignment: CrossAxisAlignment.start,
118+
children: [
119+
Text(
120+
_hovering ? 'Release to drop' : 'Drop files or text here',
121+
style: theme.textTheme.titleLarge,
122+
),
123+
const SizedBox(height: 4),
124+
Text(
125+
_hoverPosition == null
126+
? 'Supported: ${DropRegion.isSupported}'
127+
: 'At ${_hoverPosition!.dx.round()}, ${_hoverPosition!.dy.round()}',
128+
style: theme.textTheme.bodySmall,
129+
),
130+
const Divider(height: 24),
131+
Text('Drops: $_drops', style: theme.textTheme.titleMedium),
132+
const SizedBox(height: 8),
133+
Expanded(
134+
child: ListView(
135+
children: [
136+
for (final path in _droppedFiles)
137+
ListTile(
138+
dense: true,
139+
leading: const Icon(Icons.insert_drive_file_outlined),
140+
title: Text(path.split(Platform.pathSeparator).last),
141+
subtitle: Text(path),
142+
),
143+
if (_droppedText != null)
144+
ListTile(
145+
dense: true,
146+
leading: const Icon(Icons.notes),
147+
title: Text('Text: $_droppedText'),
148+
),
149+
],
150+
),
151+
),
152+
],
153+
),
154+
),
155+
);
156+
}
157+
158+
Widget _buildDragPanel(ThemeData theme) {
159+
return Column(
160+
crossAxisAlignment: CrossAxisAlignment.stretch,
161+
children: [
162+
Text('Drag out', style: theme.textTheme.titleLarge),
163+
const SizedBox(height: 12),
164+
_DragCard(
165+
icon: Icons.description_outlined,
166+
title: 'Drag this note',
167+
subtitle: _file.path.split(Platform.pathSeparator).last,
168+
child: (card) => DragOutArea(
169+
filePaths: [_file.path],
170+
onDragEnded: _onDragEnded,
171+
child: card,
172+
),
173+
),
174+
const SizedBox(height: 12),
175+
_DragCard(
176+
icon: Icons.short_text,
177+
title: 'Drag this text',
178+
subtitle: 'Hello from nativeapi',
179+
child: (card) => DragOutArea(
180+
text: 'Hello from nativeapi',
181+
onDragEnded: _onDragEnded,
182+
child: card,
183+
),
184+
),
185+
const SizedBox(height: 16),
186+
Text('Last drag: $_lastDragResult', style: theme.textTheme.bodyMedium),
187+
const Spacer(),
188+
Text(
189+
'Supported: ${DragOutArea.isSupported}',
190+
style: theme.textTheme.bodySmall,
191+
),
192+
],
193+
);
194+
}
195+
196+
void _onDragEnded(DragOperation operation) {
197+
setState(() => _lastDragResult = operation.name);
198+
}
199+
}
200+
201+
class _DragCard extends StatelessWidget {
202+
const _DragCard({
203+
required this.icon,
204+
required this.title,
205+
required this.subtitle,
206+
required this.child,
207+
});
208+
209+
final IconData icon;
210+
final String title;
211+
final String subtitle;
212+
final Widget Function(Widget card) child;
213+
214+
@override
215+
Widget build(BuildContext context) {
216+
return MouseRegion(
217+
cursor: SystemMouseCursors.grab,
218+
child: child(
219+
Card(
220+
child: ListTile(
221+
leading: Icon(icon),
222+
title: Text(title),
223+
subtitle: Text(subtitle),
224+
),
225+
),
226+
),
227+
);
228+
}
229+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
flutter/ephemeral

0 commit comments

Comments
 (0)