Introduction
The draggable_float_widget
package allows you to create a draggable widget that floats over other widgets in your Flutter app. This guide will show you how to integrate and use this package to create a draggable float widget in your Flutter app.
Content
1.Add the draggable_float_widget
dependency:
Open your pubspec.yaml
file and add the draggable_float_widget
dependency.
dependencies:
draggable_float_widget: ^latest_version
Run flutter pub get
to install the package.
2.Import the package:
Import the draggable_float_widget
package in your Dart file.
import 'package:draggable_float_widget/draggable_float_widget.dart';
3.Create a draggable float widget:
Use the DraggableFloatWidget
widget to create a draggable float widget.
DraggableFloatWidget(
child: Container(
width: 100.0,
height: 100.0,
color: Colors.blue,
child: Center(
child: Text(
'Drag Me',
style: TextStyle(color: Colors.white),
),
),
),
initialOffset: Offset(100.0, 100.0),
onPositionChanged: (offset) {
print('Position changed to: $offset');
},
)
Customize the child
widget as needed. The initialOffset
parameter specifies the initial position of the widget, and the onPositionChanged
callback is called when the widget is dragged, providing the new position offset.
4.Run the app:
Run your Flutter app to see the draggable float widget. You should be able to drag the widget around the screen.
Sample Code
import 'dart:async';
import 'package:draggable_float_widget/draggable_float_widget.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Test DraggableFloatWidget',
home: TestDraggableFloatWidget(),
);
}
}
/// root page of test
class TestDraggableFloatWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TestDraggableFloatState();
}
class _TestDraggableFloatState extends State<TestDraggableFloatWidget> {
static const double bottomBarHeight = 50;
late StreamController<OperateEvent> eventStreamController;
int _selectedIndex = 0;
List<Widget> _pages = [];
PageController _pageController = PageController();
final List<String> imageUrls = [
"https://f.hubspotusercontent30.net/hubfs/2235233/blog-import/2020/20-08-Aug/sm-icons-facebook-logo.png",
"https://cdn-icons-png.flaticon.com/512/4782/4782345.png",
"https://2235233.fs1.hubspotusercontent-na1.net/hubfs/2235233/blog-import/2022/07-22-Jul/every-social-media-logo-and-icon-in-one-handy-place-instagram.png",
"https://static.vecteezy.com/system/resources/previews/009/428/326/original/3d-social-media-icons-whatsapp-free-png.png",
"https://f.hubspotusercontent30.net/hubfs/2235233/blog-import/2020/20-08-Aug/sm-icons-facebook-logo.png",
"https://cdn-icons-png.flaticon.com/512/4782/4782345.png",
];
@override
void initState() {
super.initState();
eventStreamController = StreamController.broadcast();
_pages
..add(StackModePage(
child: defaultDragWidget,
listView: defaultList,
navigatorBarHeight: bottomBarHeight,
eventStreamController: eventStreamController,
))
..add(OverlayModePage(
child: defaultDragWidget,
listView: defaultList,
navigatorBarHeight: bottomBarHeight,
eventStreamController: eventStreamController,
));
}
Widget get defaultDragWidget => Container(
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(15),
),
alignment: Alignment.center,
padding: EdgeInsets.all(5),
child: Material(
color: Colors.transparent,
child: ListView.builder(
itemCount: imageUrls.length,
itemBuilder: (context, index) {
return Column(
children: [
Image.network(
imageUrls[index],
height: 50,
fit: BoxFit.fitWidth,
),
SizedBox(height: 15),
],
);
},
),
),
);
Widget get defaultList => Container(
child: NotificationListener(
onNotification: (notification) {
if (notification is ScrollStartNotification) {
eventStreamController.add(OperateEvent.OPERATE_HIDE);
} else if (notification is ScrollEndNotification) {
eventStreamController.add(OperateEvent.OPERATE_SHOW);
}
return true;
},
child: ListView.builder(
itemCount: Colors.accents.length,
itemBuilder: (context, index) {
Color _color = Colors.accents[index].shade100;
String _strColor = "Color(0x${_color.value.toRadixString(16).padLeft(8, '0')})";
return Container(
height: 100,
color: _color,
alignment: Alignment.center,
child: Text(
" $_strColor",
// "$index. $_strColor",
style: TextStyle(
color: Colors.black54,
fontSize: 13,
),
),
);
},
),
),
);
@override
void dispose() {
eventStreamController.close();
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
children: _pages,
controller: _pageController,
onPageChanged: (index) {
if (mounted) setState(() => _selectedIndex = index);
},
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: _buildBottomAppBar(),
);
}
Widget _buildBottomAppBar() {
return BottomAppBar(
elevation: 0,
child: Container(
height: bottomBarHeight,
child: BottomNavigationBar(
items: [
_navigationBarItem(
Icons.view_compact_outlined,
Icons.view_compact,
"Stack",
),
_navigationBarItem(
Icons.amp_stories_outlined,
Icons.amp_stories,
"Overlay",
),
],
elevation: 0,
selectedFontSize: 10,
unselectedFontSize: 10,
selectedItemColor: Colors.redAccent,
unselectedItemColor: Colors.black54,
type: BottomNavigationBarType.fixed,
currentIndex: _selectedIndex,
backgroundColor: Colors.white,
onTap: (index) => _pageController.jumpToPage(index),
),
),
);
}
BottomNavigationBarItem _navigationBarItem(
IconData icon,
IconData activeIcon,
String label,
) {
return BottomNavigationBarItem(
icon: Icon(
icon,
size: 25,
color: Colors.black54,
),
activeIcon: Icon(
activeIcon,
size: 25,
color: Colors.redAccent,
),
label: label,
);
}
}
/// stack mode
class StackModePage extends StatelessWidget {
final Widget child;
final Widget listView;
final double navigatorBarHeight;
final StreamController<OperateEvent> eventStreamController;
const StackModePage({
Key? key,
required this.child,
required this.listView,
required this.navigatorBarHeight,
required this.eventStreamController,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Color.fromARGB(255, 24, 247, 255),
title: Text(
"Float Widget Stack Mode",
style: TextStyle(color: Colors.white),
),
),
backgroundColor: Colors.amber,
body: Stack(
children: [
listView,
DraggableFloatWidget(
child: child,
height: 359,
width: 60,
eventStreamController: eventStreamController,
config: DraggableFloatWidgetBaseConfig(
isFullScreen: false,
initPositionYInTop: false,
initPositionYMarginBorder: 50,
borderBottom: navigatorBarHeight + defaultBorderWidth,
),
onTap: () => print("Drag onTap!"),
)
],
),
);
}
}
/// overlay mode
class OverlayModePage extends StatefulWidget {
final Widget child;
final Widget listView;
final double navigatorBarHeight;
final StreamController<OperateEvent> eventStreamController;
const OverlayModePage({
Key? key,
required this.child,
required this.listView,
required this.navigatorBarHeight,
required this.eventStreamController,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _OverlayModeState();
}
class _OverlayModeState extends State<OverlayModePage> {
OverlayEntry? _overlayEntry;
bool _showDraggableFloat = false;
@override
void dispose() {
_removePreviousOverlay();
super.dispose();
}
_removePreviousOverlay() {
_overlayEntry?.remove();
_overlayEntry = null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Overlay Mode",
style: TextStyle(color: Colors.white),
),
actions: [
Padding(
padding: EdgeInsets.only(right: 12),
child: InkWell(
onTap: () {
if (_showDraggableFloat) {
_removePreviousOverlay();
} else {
_showOverlay();
}
setState(() {
_showDraggableFloat = !_showDraggableFloat;
});
},
child: Icon(
_showDraggableFloat ? Icons.amp_stories_rounded : Icons.amp_stories_outlined,
color: Colors.white,
size: 28,
),
),
),
],
),
backgroundColor: Colors.grey,
body: widget.listView,
);
}
_showOverlay() {
// 1. remove previous overlay
_removePreviousOverlay();
// 2. show new overlay
_overlayEntry = OverlayEntry(builder: (context) {
return DraggableFloatWidget(
child: widget.child,
eventStreamController: widget.eventStreamController,
config: DraggableFloatWidgetBaseConfig(
initPositionYInTop: false,
initPositionYMarginBorder: 50,
borderTopContainTopBar: true,
borderBottom: widget.navigatorBarHeight + defaultBorderWidth,
),
onTap: () => print("Drag onTap!"),
);
});
/// Warning: context cannot be the context of MaterialApp
Overlay.of(context)?.insert(_overlayEntry!);
}
}
Output
Conclusion
By following these steps, you can easily integrate the draggable_float_widget
package into your Flutter app and create a draggable float widget. This can be useful for creating interactive and draggable elements in your app’s UI.