Flutter快速导航插件quick_nav的使用

Flutter快速导航插件quick_nav的使用

通过我们的Flutter插件,可以为您的应用添加一个优雅的消息气泡覆盖层和集成通知功能,确保在所有应用程序中提供无缝且互动的用户体验。

支持平台

Android

关于

Quick Nav 提供了一个简化的方法来轻松显示一个消息气泡。只需遵循以下简单的步骤即可增强您的应用功能和用户交互。

该插件专门设计用于Android,利用其独特的功能以出现在所有应用程序之上。此功能在iOS系统上不可用。

关键特性

  • 显示聊天头覆盖层: 在所有Android应用之上显示聊天头覆盖层。
  • 集成通知功能: 集成通知功能以增强用户参与度。
  • 自定义选项: 可以自定义聊天头的外观和行为。
  • 简易API: 简化API,方便在Flutter项目中集成。
  • 创建直观的响应式UI: 适用于创建直观且响应式的用户界面。

使用方法

  1. 首先,在pubspec.yaml文件中添加包
    quick_nav: ^updated_version
  1. 导航到AndroidManifest.xml文件以包含必要的权限
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
  1. 轻松使用插件并探索其全部潜力,查看示例项目,以便您可以轻松发现所有优势。

插件的力量

initService

该函数应首先被调用来激活插件,因为它需要特定的属性来配置气泡。

参数 必填 描述
screenHeight 从MediaQuery获取逻辑像素屏幕高度
chatHeadIcon 若要自定义聊天头图标,请将新图标添加到android/app/src/main/res/drawable/文件夹,并指定图像名称(不带文件格式)。若未指定自定义图标,则插件将默认使用存储在其’drawable’资源中的Android聊天头图标。
notificationIcon chatHeadIcon
当未传递图标时,默认图标为插件内的android notification icon
notificationTitle 当关闭聊天头时,我们会显示一个包含标题和正文的通知。如果不发送任何标题,默认通知标题为App Name
notificationBody notificationTitle,但默认通知正文为Your Service is still working
notificationCircleHexColor 在Android 13及以上版本中,系统会将通知图标放在圆圈内,可以通过此参数更改圆圈颜色。
但在Android 10及以下版本中,此参数用于更改通知图标

checkPermission

该函数检查应用是否具有显示其他应用之上的必要权限。它返回一个布尔值:

  • true: 权限已授予,允许启动气泡。
  • false: 权限被拒绝;在这种情况下,请先使用askPermission

askPermission

使用此函数,您将被引导至系统设置,具体为“显示其他应用之上”部分,以授予您的应用所需的权限。

startService

此函数使您可以启动显示覆盖的服务。

参数 必填 描述
notificationTitle 在启动服务之前修改通知标题。如果已在initService中指定了标题,则服务将使用该标题。
notificationBody notificationTitle

stopService

此函数使您可以停止服务,关闭覆盖。

clearNotificationService

此函数允许您从状态栏移除通知。

讨论

使用问题跟踪器报告错误和请求功能。欢迎提交拉取请求。


完整示例代码

import 'dart:async';
import 'dart:math';

import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:quick_nav/quick_nav.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  String msg = '未知';
  final _quicknavPlugin = QuickNavService.I;
  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();

  bool inTrip = false;

  bool serviceStarted = true;

  [@override](/user/override)
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print(state.name);
    print(inTrip.toString());

    if (!inTrip) return;
    switch (state) {
      case AppLifecycleState.resumed:
        clearNotificationService();
        stopService();
        break;
      case AppLifecycleState.paused:
        startService();
        break;
      default:
        break;
    }
  }

  [@override](/user/override)
  void initState() {
    clearNotificationService();
    WidgetsBinding.instance.addObserver(this);
    // 请求Android 13及以上版本的通知权限
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
        ?.requestPermission();

    // 初始化服务,不初始化服务无法启动
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      _quicknavPlugin
          .initService(
        screenHeight: MediaQuery.of(context).size.height,
      )
          .then((value) {
        serviceStarted = value ?? false;
        setState(() {});
      });
    });
    super.initState();
  }

  [@override](/user/override)
  void didChangeMetrics() {
    stopService();
    super.didChangeMetrics();
  }

  [@override](/user/override)
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  Future<void> startService() async {
    bool serviceWorks;
    try {
      final double navigationBar = await getNavHeight();
      if (!mounted) return;
      final double density = MediaQuery.of(context).devicePixelRatio;
      final double screenHeight = MediaQuery.of(context).size.height;
      final double screenWidth = MediaQuery.of(context).size.width * density;
      print({
        'screenWidth-P.P': screenWidth,
        'screenHeight-L.P': screenHeight,
        'density-DPR': density,
        'navigationBarHeight': navigationBar,
      }.toString());

      serviceWorks = await _quicknavPlugin.startService() ?? false;

      if (!serviceWorks) return;
    } on PlatformException catch (e) {
      serviceWorks = false;
      print(e.message.toString());
    }
    if (!mounted) return;

    setState(() {
      msg = (serviceWorks
          ? "成功启动聊天头服务"
          : "启动聊天头服务时出错");
    });
  }

  Future<void> stopService() async {
    bool serviceStop;
    try {
      serviceStop = await _quicknavPlugin.stopService() ?? false;
    } on PlatformException catch (e) {
      print(e.message.toString());
      serviceStop = false;
    }
    if (!mounted) return;

    setState(() {
      msg = (serviceStop
          ? "成功停止聊天头服务"
          : "停止聊天头服务时出错");
    });
  }

  Future<void> clearNotificationService() async =>
      _quicknavPlugin.clearNotificationService();

  Future<double> getNavHeight() async {
    final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
    final screenHeight = MediaQuery.of(context).size.height;

    final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
    final AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
    final deviceHeight = androidInfo.displayMetrics.heightPx;

    final androidNavHeight = deviceHeight / devicePixelRatio - screenHeight;
    return androidNavHeight;
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        useMaterial3: true,
      ),
      home: Scaffold(
        backgroundColor: Colors.white,
        appBar: AppBar(
          title: const Text('Quick Nav 插件'),
        ),
        body: SizedBox(
          width: MediaQuery.of(context).size.width,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('$msg\n'),
              if (serviceStarted) ...[
                const SizedBox(height: 20),
                ElevatedButton(
                  onPressed: () async {
                    if (!inTrip) {
                      final hasAPermission =
                          await _quicknavPlugin.checkPermission();

                      if (!(hasAPermission ?? true)) {
                        await _quicknavPlugin.askPermission();
                        return;
                      }
                    }
                    inTrip = !inTrip;

                    print(inTrip.toString());
                    setState(() {});
                  },
                  child: Text(inTrip ? '停止行程' : '开始行程'),
                ),
              ]
            ],
          ),
        ),
      ),
    );
  }
}

class QuickNavService {
  QuickNavService._();
  static QuickNavService I = QuickNavService._();

  final QuickNav _quicknav = QuickNav.I;

  Future<bool?> initService({
    required double screenHeight,
    String? chatHeadIcon,
    String? notificationIcon,
    String? notificationTitle,
    int? notificationCircleHexColor,
    String? notificationBody,
  }) =>
      _quicknav.initService(
        screenHeight: screenHeight,
        chatHeadIcon: chatHeadIcon,
        notificationIcon: notificationIcon,
        notificationTitle: notificationTitle,
        notificationCircleHexColor: notificationCircleHexColor,
        notificationBody: notificationBody,
      );

  Future<bool?> startService({String? notificationTitle}) =>
      _quicknav.startService(
        notificationTitle: notificationTitle,
      );

  Future<bool?> stopService() => _quicknav.stopService();

  Future<bool?> checkPermission() => _quicknav.checkPermission();

  Future<bool?> askPermission() => _quicknav.askPermission();

  Future<bool?> clearNotificationService() =>
      _quicknav.clearServiceNotification();
}

更多关于Flutter快速导航插件quick_nav的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter快速导航插件quick_nav的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用quick_nav插件进行快速导航的代码示例。quick_nav插件允许开发者在应用中实现快速跳转功能,通常用于实现类似“返回顶部”或快速导航到某个特定部分的功能。

首先,确保你已经在pubspec.yaml文件中添加了quick_nav依赖:

dependencies:
  flutter:
    sdk: flutter
  quick_nav: ^最新版本号  # 请替换为实际最新版本号

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

接下来是一个简单的示例,展示如何在Flutter应用中使用quick_nav插件:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Quick Nav Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: QuickNavDemo(),
    );
  }
}

class QuickNavDemo extends StatefulWidget {
  @override
  _QuickNavDemoState createState() => _QuickNavDemoState();
}

class _QuickNavDemoState extends State<QuickNavDemo> {
  final QuickNavController _quickNavController = QuickNavController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Quick Nav Demo'),
      ),
      body: Column(
        children: <Widget>[
          QuickNavButton(
            controller: _quickNavController,
            targetId: 'section1',
            child: IconButton(
              icon: Icon(Icons.arrow_upward),
              onPressed: () => _quickNavController.scrollToTarget('section1'),
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: 20,
              itemBuilder: (context, index) {
                String sectionId = 'section$index';
                return Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      QuickNavTarget(
                        id: sectionId,
                        child: Text(
                          'Section $index',
                          style: TextStyle(fontSize: 20),
                        ),
                      ),
                      SizedBox(height: 20),
                    ],
                  ),
                );
              },
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _quickNavController.scrollToTop(),
        tooltip: 'Scroll to Top',
        child: Icon(Icons.arrow_upward),
      ),
    );
  }

  @override
  void dispose() {
    _quickNavController.dispose();
    super.dispose();
  }
}

代码说明:

  1. 依赖导入

    • 导入flutter/material.dartquick_nav/quick_nav.dart
  2. 应用入口

    • 使用MaterialApp作为应用入口,并设置homeQuickNavDemo
  3. QuickNavDemo组件

    • 创建一个StatefulWidget,并在其状态类中初始化QuickNavController
    • build方法中,使用Column布局,包含一个QuickNavButton和一个ListView
    • QuickNavButton用于触发快速导航到指定的targetId。在这个例子中,我们创建了一个简单的IconButton,当点击时,它会调用_quickNavController.scrollToTarget('section1')
    • ListView生成了20个部分,每个部分都有一个QuickNavTarget,其id设置为section$index
    • floatingActionButton设置为一个FloatingActionButton,点击时会调用_quickNavController.scrollToTop(),实现快速滚动到顶部。
  4. 资源释放

    • dispose方法中释放QuickNavController资源。

这个示例展示了如何使用quick_nav插件在Flutter应用中实现快速导航功能。你可以根据需要调整代码,以适应你的具体应用场景。

回到顶部