Flutter浮动窗口管理插件floating_windows的使用

Flutter浮动窗口管理插件floating_windows的使用

特性

  • 屏幕顶部浮动的小部件
  • 可调整大小和位置的浮动小部件
  • 可选择在树结构内浮动的空间(可选)
  • 带有边距的限制边界
  • 在弹出和关闭屏幕时进行状态管理(需要RouteObserver来管理弹出)

FloatingWindows

开始使用

在你的pubspec.yaml文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  floating_windows: ^1.0.0

导入包:

import 'package:floating_windows/floating_windows.dart';

使用方法

首先创建一个FloatingOverlayController实例。以下是绝对大小控制的例子:

final controller1 = FloatingOverlayController.absoluteSize(
  maxSize: const Size(800, 800),
  minSize: const Size(400, 300),
  start: Offset.zero,
  padding: const EdgeInsets.all(20.0),
  constrained: true,
);
final controller2 = FloatingOverlayController.absoluteSize(
  maxSize: const Size(800, 800),
  minSize: const Size(400, 300),
  start: const Offset(100, 100),
  padding: const EdgeInsets.all(20.0),
  constrained: true,
);
final controller3 = FloatingOverlayController.absoluteSize(
  maxSize: const Size(800, 800),
  minSize: const Size(300, 500),
  start: const Offset(300, 300),
  padding: const EdgeInsets.all(20.0),
  constrained: true,
);

也可以使用相对大小控制:

final controller = FloatingOverlayController.relativeSize(
  maxScale: 2.0,
  minScale: 1.0,
  start: Offset.zero,
  padding: const EdgeInsets.all(20.0),
  constrained: true,
);

然后将FloatingOverlay插入到你的Widget树中,并给它控制器、子组件和浮动子组件:

FloatingOverlay(
  controllers: [controller1, controller2, controller3],
  floatingChildren: [
    SizedBox(
      width: 400,
      height: 300,
      child: Container(
        decoration: BoxDecoration(
          color: Theme.of(context).primaryColor,
          border: Border.all(
            color: Colors.black,
            width: 5.0,
          ),
        ),
      ),
    ),
    SizedBox(
      width: 400,
      height: 300,
      child: Container(
        decoration: BoxDecoration(
          color: Colors.amber,
          border: Border.all(
            color: Colors.black,
            width: 5.0,
          ),
        ),
      ),
    ),
    SizedBox(
      width: 300,
      height: 500,
      child: Container(
        decoration: BoxDecoration(
          color: Colors.red,
          border: Border.all(
            color: Colors.black,
            width: 5.0,
          ),
        ),
      ),
    ),
  ],
  child: Container(),
)

你可以通过控制器来显示或隐藏浮动子组件:

controller.hide();
controller.isFloating;
controller.show();
controller.toggle();

完整示例Demo

以下是一个完整的示例,展示了如何使用floating_windows插件:

import 'package:flutter/material.dart';
import 'package:floating_windows/floating_windows.dart';

import 'package:provider/provider.dart';
import 'dart:math' as math;

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    final routeObserver = RouteObserver();
    return MaterialApp(
      title: 'Floating Windows Example',
      navigatorObservers: [routeObserver],
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Provider<RouteObserver>(
        create: (_) => routeObserver,
        child: const HomePage(),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    final controller1 = FloatingOverlayController.absoluteSize(
      maxSize: const Size(800, 800),
      minSize: const Size(400, 300),
      start: Offset.zero,
      padding: const EdgeInsets.all(20.0),
      constrained: true,
    );
    final controller2 = FloatingOverlayController.absoluteSize(
      maxSize: const Size(800, 800),
      minSize: const Size(400, 300),
      start: const Offset(100, 100),
      padding: const EdgeInsets.all(20.0),
      constrained: true,
    );
    final controller3 = FloatingOverlayController.absoluteSize(
      maxSize: const Size(800, 800),
      minSize: const Size(300, 500),
      start: const Offset(300, 300),
      padding: const EdgeInsets.all(20.0),
      constrained: true,
    );
    final routeObserver = Provider.of<RouteObserver>(context, listen: false);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Floating Windows Example'),
        centerTitle: true,
      ),
      body: FloatingOverlay(
        routeObserver: routeObserver,
        controllers: [controller1, controller2, controller3],
        floatingChildren: [
          SizedBox(
            width: 400,
            height: 300,
            child: Container(
              decoration: BoxDecoration(
                color: Theme.of(context).primaryColor,
                border: Border.all(
                  color: Colors.black,
                  width: 5.0,
                ),
              ),
            ),
          ),
          SizedBox(
            width: 400,
            height: 300,
            child: Container(
              decoration: BoxDecoration(
                color: Colors.amber,
                border: Border.all(
                  color: Colors.black,
                  width: 5.0,
                ),
              ),
            ),
          ),
          SizedBox(
            width: 300,
            height: 500,
            child: Container(
              decoration: BoxDecoration(
                color: Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0),
                border: Border.all(
                  color: Colors.black,
                  width: 5.0,
                ),
              ),
            ),
          ),
        ],
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              CustomButton(
                title: 'Toggle Overlay 1',
                onPressed: () {
                  controller1.toggle();
                },
              ),
              CustomButton(
                title: 'Toggle Overlay 2',
                onPressed: () {
                  controller2.toggle();
                },
              ),
              CustomButton(
                title: 'Toggle Overlay 3',
                onPressed: () {
                  controller3.toggle();
                },
              ),
              CustomButton(
                title: 'Set Screen Center Offset',
                onPressed: () {
                  final size = MediaQuery.of(context).size;
                  final rect = Rect.fromPoints(
                    Offset.zero,
                    Offset(size.width, size.height),
                  );
                  controller1.offset = rect.center;
                },
              ),
              CustomButton(
                title: 'Set Scale to 2.0',
                onPressed: () {
                  controller1.scale = 2.0;
                },
              ),
              CustomButton(
                title: 'New Page',
                onPressed: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(builder: (context) => const NewPage()),
                  );
                },
              ),
              CustomButton(
                title: 'New Page with AnimationController',
                onPressed: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => Provider<RouteObserver>(
                        create: (_) => routeObserver,
                        child: const AnimationPage(),
                      ),
                    ),
                  );
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class CustomButton extends StatelessWidget {
  const CustomButton({
    Key? key,
    required this.onPressed,
    required this.title,
  }) : super(key: key);

  final VoidCallback onPressed;
  final String title;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ElevatedButton(
        child: Text(title),
        onPressed: onPressed,
      ),
    );
  }
}

class NewPage extends StatelessWidget {
  const NewPage({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('New Page'), centerTitle: true),
    );
  }
}

class AnimationPage extends StatefulWidget {
  const AnimationPage({Key? key}) : super(key: key);

  [@override](/user/override)
  _AnimationPageState createState() => _AnimationPageState();
}

class _AnimationPageState extends State<AnimationPage> with SingleTickerProviderStateMixin {
  late final AnimationController animationController;
  late final FloatingOverlayController controller;

  [@override](/user/override)
  void initState() {
    animationController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1),
    );

    controller = FloatingOverlayController.absoluteSize(
      maxSize: const Size(200, 200),
      minSize: const Size(100, 100),
      padding: const EdgeInsets.all(20.0),
      constrained: true,
    );
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Floating Overlay Example'),
        centerTitle: true,
      ),
      body: FloatingOverlay(
        routeObserver: Provider.of<RouteObserver>(context, listen: false),
        controllers: [controller],
        floatingChildren: [
          SizedBox.square(
            dimension: 100.0,
            child: Container(
              decoration: BoxDecoration(
                color: Theme.of(context).primaryColor,
                border: Border.all(
                  color: Colors.black,
                  width: 5.0,
                ),
              ),
            ),
          ),
        ],
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: ElevatedButton(
                  child: const Text('Toggle Overlay'),
                  onPressed: () {
                    controller.toggle();
                  },
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: ElevatedButton(
                  child: const Text('New Page'),
                  onPressed: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(builder: (context) => const NewPage()),
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

更多关于Flutter浮动窗口管理插件floating_windows的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter浮动窗口管理插件floating_windows的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter应用中使用floating_windows插件来管理浮动窗口的示例代码。floating_windows插件允许你在Android和iOS平台上创建和管理浮动窗口(也称为小窗口或悬浮窗)。

首先,你需要在你的pubspec.yaml文件中添加floating_windows依赖:

dependencies:
  flutter:
    sdk: flutter
  floating_windows: ^0.3.0  # 请注意版本号,使用最新版本

然后运行flutter pub get来获取依赖。

接下来是一个简单的示例,展示了如何使用floating_windows插件来创建一个浮动窗口:

import 'package:flutter/material.dart';
import 'package:floating_windows/floating_windows.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Floating Windows Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: _createFloatingWindow,
            child: Text('Create Floating Window'),
          ),
        ),
      ),
    );
  }

  void _createFloatingWindow() async {
    // 检查平台是否支持浮动窗口
    if (!await FloatingWindows.isSupported) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Floating windows are not supported on this platform.')),
      );
      return;
    }

    // 创建浮动窗口
    final windowInfo = await FloatingWindows.create({
      'width': 300,
      'height': 400,
      'x': 0, // 窗口初始位置的x坐标
      'y': 0, // 窗口初始位置的y坐标
    });

    // 构建浮动窗口的内容
    final floatingWidget = MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Floating Window'),
        ),
        body: Center(
          child: Text('This is a floating window!'),
        ),
      ),
    );

    // 显示浮动窗口
    await FloatingWindows.show(
      windowInfo.id,
      floatingWidget,
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个按钮。点击按钮时,会检查当前平台是否支持浮动窗口。如果支持,则创建一个浮动窗口并显示它。

注意事项

  1. 权限:在Android上,显示浮动窗口通常需要SYSTEM_ALERT_WINDOW权限。你可能需要引导用户到系统的设置页面手动授予该权限。
  2. 平台差异:iOS和Android对浮动窗口的支持和限制有所不同。在实际开发中,你可能需要根据平台差异做额外的适配。
  3. 插件版本:插件的API可能会随着版本更新而变化,请参考最新的插件文档以获取最新的使用方法和最佳实践。

这个示例代码提供了一个基本的起点,你可以根据自己的需求进一步扩展和定制浮动窗口的功能和外观。

回到顶部