Implementing Draggable Float Widget in Flutter ?

Spread the love
Implementing Draggable Float Widget in Flutter
Implementing Draggable Float Widget in Flutter

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

Implementing Draggable Float Widget in Flutter
Implementing Draggable Float Widget in Flutter

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.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *