Day22 - Flutter - 混合开发(上)

简介: Day22 - Flutter - 混合开发

概述

  • 调用原生功能
  • 嵌入原有项目
  • Flutter模块调试


一、调用原生功能



  • 1.1、Camera某些应用程序可能需要使用移动设备进行拍照或者选择相册中的照片,Flutter官方提供了插件:image_picker
  • 1.1.1、添加依赖
    添加对image_picker的依赖:https://pub.dev/packages/image_picker,在项目的 pubspec.ymal 里面添加下面的依赖即可,然后执行右上角的 Pub get


dependencies:
     image_picker: ^0.6.7+1


  • 1.1.2、平台配置
    对iOS平台,想要访问相册或者相机,需要获取用户的允许:
    依然是修改 info.plist 文件:/ios/Runner/Info.plist
    添加对相册的访问权限:Privacy - Photo Library Usage Description
    添加对相机的访问权限:Privacy - Camera Usage Description


image.png

  • 拓展:其他的权限
  • 相机权限:Privacy - Camera Usage Description 是否允许此App使用你的相机?
  • 相册权限:Privacy - Photo Library Usage Description 是否允许此App访问你的媒体资料库?
  • 通讯录权限:Privacy - Contacts Usage Description 是否允许此App访问你的通讯录?
  • 蓝牙权限:Privacy - Bluetooth Peripheral Usage Description 是否许允此App使用蓝牙?
  • 使用期间定位权限:Privacy - Location When In Use Usage Description 是否允许此App使用定位服务?
  • 始终定位权限:Privacy - Location Always Usage Description 是否允许此App始终使用定位服务?
  • 语音转文字权限:Privacy - Speech Recognition Usage Description 是否允许此App使用语音识别?
  • 日历权限:Privacy - Calendars Usage Description 是否允许此App使用日历?
  • 健康—读取数据: Privacy - Health Share Usage Description 是否允许此App读取健康数据?
  • 健康—写入数据: Privacy - Health Share Usage Description 是否允许此App写入健康数据?
  • 读取HomeKit: Privacy - HomeKit Usage Description 是否允许此App访问HomeKit?
  • 麦克风:Privacy - Microphone Usage Description 是否允许此App访问麦克风?
  • 提醒事项: Privacy - Reminders Usage Description 是否允许此App访问提醒事项?
  • 运动与健身: Privacy - Motion Usage Description 是否允许此App访问运动与健身?
  • 面部ID权限: Privacy - Face ID Usage Description 是否允许此App访问Face ID?


之后选择相册或者访问相机时,会弹出如下的提示框:

image.png


image.png


1.1.3、代码实现


image_picker 的核心代码是 getImage 方法:

可以传入数据源、图片的大小、质量、前置后置摄像头等

数据源是必传参数:ImageSource 枚举类型: camera:相机gallery:相册

Future<PickedFile> getImage({
   @required ImageSource source,
   double maxWidth,
   double maxHeight,
   int imageQuality,
   // 默认后置摄像头
   CameraDevice preferredCameraDevice = CameraDevice.rear,
}) {
   return platform.pickImage(
      source: source,
      maxWidth: maxWidth,
      maxHeight: maxHeight,
      imageQuality: imageQuality,
      preferredCameraDevice: preferredCameraDevice,
   );
}

案例演练:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class JKCameraScreen extends StatefulWidget {
   @override
   _JKCameraScreenState createState() => _JKCameraScreenState();
}
class _JKCameraScreenState extends State<JKCameraScreen> {
   PickedFile _imageFile;
   final ImagePicker _picker = ImagePicker();
   @override
   Widget build(BuildContext context) {
        return Center(
           child: Column(
              children: [
                 RaisedButton(
                    child: Text('选择一个相册'),
                    onPressed: _pickImage
                 ),
                 _imageFile == null ? Text('请选择一张照片') : Image.file(File(_imageFile.path))
              ],
           ),
        );
   }
   void _pickImage() async {
        print('选择相册');
        PickedFile pickedFile = await _picker.getImage(source: ImageSource.gallery);
        setState(() {
            _imageFile = pickedFile;
        });
   }
}


image.png


image.png


1.2、电池信息

某些原生的信息,如果没有很好的插件,我们可以通过、platform channels(平台通道) 来获取信息。

  • 1.2.1、平台通过介绍平台通过是如何工作的呢?
  • 消息使用platform channels(平台通道)在客户端(UI)和宿主(平台)之间传递;
  • 消息和响应以异步的形式进行传递,以确保用户界面能够保持响应;


image.png


调用过程大致如下:

  • 1.客户端(Flutter端)发送与方法调用相对应的消息
  • 2.平台端(iOS、Android端)接收方法,并返回结果;
  • iOS端通过FlutterMethodChannel做出响应;
  • Android端通过MethodChannel做出响应;

Flutter、iOS、Android端数据类型的对应关系:


image.png

  • 1.2.2、创建测试项目我们这里创建一个获取电池电量信息的项目,分别通过iOS和Android原生代码来获取对应的信息:


  • 创建方式一:默认创建方式,目前默认创建的Flutter项目,对应iOS的编程语言是Swift,对应Android的编程语言是kotlin


flutter create batterylevel
  • 创建方式二:指定编程语言,如果我们希望指定编程语言,比如iOS编程语言为Objective-C,Android的编程语言为Java


flutter create -i objc -a java batterylevel2
  • 提示:i代表 iOSa 代表 android


  • 1.2.3、写Dart代码在Dart代码中,我们需要创建一个MethodChannel对象:
  • 创建该对象时,需要传入一个name,该name是区分多个通信的名称
  • 可以通过调用该对象的invokeMethod来给对应的平台发送消息进行通信
  • 该调用是异步操作,需要通过await获取then回调来获取结果


import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            // 启动要显示的界面
            home: HomePage(),
        );
    }
}
class HomePage extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
       return Scaffold(
           appBar: AppBar(
              title: Text("原生电池的调用"),
           ),
           body: JKBatteryLevel(),
       );
   }
}
class JKBatteryLevel extends StatefulWidget {
    @override
    _JKBatteryLevelState createState() => _JKBatteryLevelState();
}
class _JKBatteryLevelState extends State<JKBatteryLevel> {
     // 定义一个平台通道
     static const platform1 = const MethodChannel('com.jk/battery');
     int _batterylevel = 0;
     @override
     Widget build(BuildContext context) {
        return Center(
           child: Column(
              children: [
                  RaisedButton(
                       child: Text('获取剩余电量'),
                       onPressed: _buildLevelInfo
                  ),
                  Text('电量:${_batterylevel}')
              ],
           ),
        );
    }
    void _buildLevelInfo() async {
         // 调用原生的电池信息
         final result = await platform1.invokeMethod('getBatteryInfo');
          setState(() {
              _batterylevel = result;
          });
    }
}
  • 当我们通过 platform.invokeMethod调用对应平台方法时,需要在对应的平台实现其操作:
    iOS 中可以通过 Objective-CSwift 来实现
    Android 中可以通过 Java 或者 Kotlin 来实现


  • 1.2.4、编写iOS代码
  • <1>、Swift 代码,在 AppDelegate.swift 里面写代码


import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
       didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
       // 实现获取电量信息的功能
       // 1、获取FlutterViewController
       let flutterController: FlutterViewController = window.rootViewController as! FlutterViewController
       // 2、创建 FlutterMethodChannel
       /**
         name:static const platform1 = const MethodChannel('com.jk/battery'); 的 com.jk/battery,名字自己定义: 域名/名字
         binaryMessenger:  二进制消息
        */
       let channel = FlutterMethodChannel(name: "com.jk/battery", binaryMessenger: flutterController.binaryMessenger);
       // 3.监听channnel方法
       channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
           guard call.method == "getBatteryInfo" else {
                // 找不到该方法
                result(FlutterMethodNotImplemented)
                return;
           }
           let device = UIDevice.current
           // 电池电量的探测,设置为true,才能更好的获取电量
           device.isBatteryMonitoringEnabled = true
           if device.batteryState == .unknown {
                result(FlutterError(code: "Unknown", message: "Battery is unknown", details: nil))
            } else {
                result(Int(device.batteryLevel * 100))
            }
        }
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
   }
}


image.png


image.png


  • <2>、OC 代码


#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 1.获取FlutterViewController(是应用程序的默认Controller)
    FlutterViewController *flutterController = (FlutterViewController *)self.window.rootViewController;
    // 2.获取MethodChannel(方法通道)
    FlutterMethodChannel *batteryChannel = [FlutterMethodChannel methodChannelWithName:@"com.jk/battery" binaryMessenger:flutterController.binaryMessenger];
    // 3.监听方法调用(会调用传入的回调函数)
    __weak typeof(self) weakSelf = self;
    [batteryChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        // 3.1.判断是否是getBatteryInfo的调用
        if ([@"getBatteryInfo" isEqualToString:call.method]) {
            // 1.iOS中获取信息的方式
            int batteryLevel = [weakSelf getBatteryLevel];
            // 2.如果没有获取到,那么返回给Flutter端一个异常
            if (batteryLevel == -1) {
                result([FlutterError errorWithCode:@"UNAVAILABLE"
                        message:@"Battery info unavailable"
                        details:nil]);
            } else {
                // 3.通过result将结果回调给Flutter端
                result(@(batteryLevel));
            }
        } else {
             // 3.2.如果调用的是getBatteryInfo的方法, 那么通过封装的另外一个方法实现回调
            result(FlutterMethodNotImplemented);
        }
     }];
     [GeneratedPluginRegistrant registerWithRegistry:self];
     // Override point for customization after application launch.
     return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (int)getBatteryLevel {
    // 获取信息的方法
    UIDevice* device = UIDevice.currentDevice;
    device.batteryMonitoringEnabled = YES;
    if (device.batteryState == UIDeviceBatteryStateUnknown) {
         return -1;
    } else {
         return (int)(device.batteryLevel * 100);
    }
}
@end
  • 1.2.5、编写 Android 代码
  • <1>、Ktolin 代码


package com.example.batterylevel
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
   private val CHANNEL = "com.jk/battery"
   override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
       super.configureFlutterEngine(flutterEngine)
       // 1.创建MethodChannel对象
       val methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
       // 2.添加调用方法的回调
       methodChannel.setMethodCallHandler { call, result ->
           if (call.method == "getBatteryInfo") {
                // 2.1.1.调用另外一个自定义方法回去电量信息
                val batteryLevel = getBatteryLevel()
                // 2.1.2. 判断是否正常获取到
                if (batteryLevel != -1) {
                     // 获取到返回结果
                     result.success(batteryLevel)
                } else {
                     // 获取不到抛出异常
                     result.error("UNAVAILABLE", "Battery level not available.", null)
                }
           } else {
                // 2.2.如果调用的方法是getBatteryInfo,那么正常执行
                result.notImplemented()
           }
       }
    }
    private fun getBatteryLevel(): Int {
        val batteryLevel: Int
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
           val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
           batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
        } else {
           val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
           batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
        }
        return batteryLevel
    }
}


image.png

image.png


目录
相关文章
|
2月前
|
Android开发 iOS开发 容器
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
|
8天前
|
传感器 前端开发 Android开发
在 Flutter 开发中,插件开发与集成至关重要,它能扩展应用功能,满足复杂业务需求
在 Flutter 开发中,插件开发与集成至关重要,它能扩展应用功能,满足复杂业务需求。本文深入探讨了插件开发的基本概念、流程、集成方法、常见类型及开发实例,如相机插件的开发步骤,同时强调了版本兼容性、性能优化等注意事项,并展望了插件开发的未来趋势。
22 2
|
6月前
|
开发框架 前端开发 测试技术
Flutter开发常见问题解答
Flutter开发常见问题解答
|
2月前
|
开发者
鸿蒙Flutter实战:07-混合开发
鸿蒙Flutter混合开发支持两种模式:1) 基于har包,便于主项目开发者无需关心Flutter细节,但不支持热重载;2) 基于源码依赖,利于代码维护与热重载,需配置Flutter环境。项目结构包括AppScope、flutter_module等目录,适用于不同开发需求。
80 3
|
26天前
|
传感器 开发框架 物联网
鸿蒙next选择 Flutter 开发跨平台应用的原因
鸿蒙(HarmonyOS)是华为推出的一款旨在实现多设备无缝连接的操作系统。为了实现这一目标,鸿蒙选择了 Flutter 作为主要的跨平台应用开发框架。Flutter 的跨平台能力、高性能、丰富的生态支持和与鸿蒙系统的良好兼容性,使其成为理想的选择。通过 Flutter,开发者可以高效地构建和部署多平台应用,推动鸿蒙生态的快速发展。
170 0
|
28天前
|
Dart 安全 UED
Flutter&鸿蒙next中的表单封装:提升开发效率与用户体验
在移动应用开发中,表单是用户与应用交互的重要界面。本文介绍了如何在Flutter中封装表单,以提升开发效率和用户体验。通过代码复用、集中管理和一致性的优势,封装表单组件可以简化开发流程。文章详细讲解了Flutter表单的基础、封装方法和表单验证技巧,帮助开发者构建健壮且用户友好的应用。
65 0
|
2月前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
84 7
|
2月前
|
编解码 Dart API
鸿蒙Flutter实战:06-使用ArkTs开发Flutter鸿蒙插件
本文介绍了如何开发一个 Flutter 鸿蒙插件,实现 Flutter 与鸿蒙的混合开发及双端消息通信。通过定义 `MethodChannel` 实现 Flutter 侧的 token 存取方法,并在鸿蒙侧编写 `EntryAbility` 和 `ForestPlugin`,使用鸿蒙的首选项 API 完成数据的读写操作。文章还提供了注意事项和参考资料,帮助开发者更好地理解和实现这一过程。
74 0
|
2月前
|
Dart Android开发
鸿蒙Flutter实战:03-鸿蒙Flutter开发中集成Webview
本文介绍了在OpenHarmony平台上集成WebView的两种方法:一是使用第三方库`flutter_inappwebview`,通过配置pubspec.lock文件实现;二是编写原生ArkTS代码,自定义PlatformView,涉及创建入口能力、注册视图工厂、处理方法调用及页面构建等步骤。
61 0
|
3月前
|
JSON Dart Java
flutter开发多端平台应用的探索
flutter开发多端平台应用的探索
53 6