draggable_float_widget 0.1.0 copy "draggable_float_widget: ^0.1.0" to clipboard
draggable_float_widget: ^0.1.0 copied to clipboard

This is a draggable and floating Flutter widget, which can control its visibility through the scrolling event of ScrollView.

example/lib/main.dart

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(
      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();

  @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: Text(
            "Drag\nWidget",
            style: TextStyle(
              fontSize: 15,
              color: Colors.white,
              fontWeight: FontWeight.bold,
            ),
            textAlign: TextAlign.center,
          ),
        ),
      );

  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: 50,
                color: _color,
                alignment: Alignment.center,
                child: Text(
                  "$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(
        title: Text(
          "Stack Mode",
          style: TextStyle(color: Colors.white),
        ),
      ),
      backgroundColor: Colors.grey,
      body: Stack(
        children: [
          listView,
          DraggableFloatWidget(
            child: child,
            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!);
  }
}
80
likes
150
points
215k
downloads

Publisher

unverified uploader

Weekly Downloads

This is a draggable and floating Flutter widget, which can control its visibility through the scrolling event of ScrollView.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on draggable_float_widget