场景描述:
- Flutter中嵌入一个地图页,并叠加一个底部操作栏,呈现悬浮状态,地图页可拖动、缩放等一些列功能,底部操作栏的一些列操作不影响地图页的操作
简化复现:
- 使用
Flutter中使用Stack布局利用UiKitView嵌套原生MKMapView,再堆叠一个button(或者其他widget)
问题描述
- 第一次拖动、缩放原生部分map一切正常,但是再点击
Flutter部分button(或者其他widget相同)后,原生map部分就失去所有响应
补充:
1、UiKitView是Flutter一种widget,相当于一个容器,可以嵌入iOS平台原生View,自带点击相关手势、参数传递、参数编码等功能。
2、基本功能需要在指定viewType,这是一个String类型的参数,是iOS上View类型的唯一标识,两端必须保持一致,这个id需要注册到一个遵循FlutterPlatformViewFactory协议的类上。
3、除此之外,还需要一个遵守FlutterPlatformView协议的类,就是在这个类里,有你需要的原生View,不管是Label、Button、UIScrollView或者是MKMapView。
import Flutter
import UIKit
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// withId就是flutter那边的viewType参数,需要保持一致!!!
// 将MapViewFactory类实例注册注册这个id,MapViewFactory类需要实现FlutterPlatformViewFactory协议
registrar(forPlugin: "bug.demo.UiKitView")?.register(MapViewFactory(),
withId: "bug.demo.MapView")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
class MapViewFactory: NSObject, FlutterPlatformViewFactory {
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
// 实例化一个MapPlatformView
MapPlatformView(frame, viewId, args)
}
}
class MapPlatformView: NSObject, FlutterPlatformView {
var frame: CGRect
var viewId: Int64
var args: Any?
var mapView: MKMapView //地图view
init(_ frame: CGRect, _ viewId: Int64, _ args: Any?) {
self.frame = frame
self.viewId = viewId
self.args = args
mapView = MKMapView(frame: frame)
}
func view() -> UIView {
mapView
}
}
简单代码还原
Flutter端:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
createState() => _MyApp();
}
class _MyApp extends State<MyApp> {
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) => MaterialApp(
title: 'UiKitView Bug',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
// 使用stack布局
home: _byStack(context),
);
// stack布局
Widget _byStack(BuildContext context) => Scaffold(
body: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
UiKitView(
// viewType是一个String
viewType: "bug.demo.MapView",
),
_buildButton(context),
],
),
);
// column布局
Widget _byColumn(BuildContext context) => Scaffold(
body: Column(
children: [
Expanded(
child: UiKitView(
viewType: "bug.demo.MapView",
),
),
_buildButton(context),
],
),
);
Widget _buildButton(BuildContext context) => MaterialButton(
color: Colors.blue,
onPressed: () {},
child: Text("点我进行下一步操作!"),
);
}
经过测试,使用Column布局不会出现上述问题,下面分别是Stack和Column布局的模拟器截图:
解决
- 在
AppDelegate中注册的时候,加上下面一个Policy:FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded,这个政策意味着,在原生View方经过一个完整的响应者链后(也就是touchesEnded方法调用后),Flutter才会阻止原生View上所有手势识别。 - 与之对应的还有一个
FlutterPlatformViewGestureRecognizersBlockingPolicyEager,它意味着只有touchesBegan才会被识别,除此之外都会被Flutter方阻止。
FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded
registrar(forPlugin: "bug.demo.UiKitView")?.register(MapViewFactory(),
withId: "bug.demo.MapView", gestureRecognizersBlockingPolicy: FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded)