Flutter未知功能插件surface的介绍与使用
Flutter未知功能插件surface的介绍与使用
Surface
是一个可变形、分层、内在动画的容器小部件,提供方便访问模糊处理的 ImageFilter
、Material
的 InkResponse
和 HapticFeedback
。
🎊 BouncyBall
BouncyBall
是一个令人愉悦的弹跳反应,对用户输入在 Material
上的位置进行镜像。该功能目前仍在开发中。
📚 SurfaceLayer
容器分割
SurfaceLayer
提供了强大的定制功能,适用于全应用风格设置或动态更改。
- 支持
Color
和Gradient
在BASE
和MATERIAL
层。 - 支持三种不同的过滤器及其强度。
- 边距可以通过
Shape.padLayer
进行分配。
🔰 自定义 SurfaceShape
- 手动
Shape
定义正在开发中。 - 由
CornerSpec
定义并生成SurfaceShape
。 - 可选提供边框或缩放。
🔲 Peek
Peek
是 MATERIAL
边距或“边框”,大小由参数 Peek.peek
设置。
- 通过传递
Peek.alignment
和调整Peek.ratio
来给选定的边特殊处理,使其看起来更厚。
👆 TapSpec
如果 Surface
是 TapSpec.tappable
,则:
TapSpec.onTap
回调可用。- 可以提供颜色来定制
InkResponse
。 - 考虑使用
TapSpec.providesFeedback
来进行触觉反馈。 - 使用
BouncyBall
的splashFactory
,可以选择自己的splashFactory
或默认为Theme
的。
🔬 定义 SurfaceFX
的过滤器
- 在配置的
Filter.filteredLayers
Set
中 - 其半径(强度)通过
Filter.radiusMap
映射。 SurfaceLayer.BASE
过滤器可以通过Surface.margin
延伸。SurfaceFX
类型用于基于SurfaceLayer
和当前Layer
的半径传递自定义效果。
🤸♂️ FX
开放式扩展
目前仅负责 FX.blurry
,这是 Filter
的默认 ImageFilter
。
📖 参考
- 🌟
Surface
- 一个可变形、分层、动画的容器小部件。 - 🔰
Shape
- 由CornerSpec
定义并生成SurfaceShape
。 - 🔲
Peek
- 一个具有可选参数的对象,用于自定义Surface
的“peek”。 - 👆
TapSpec
- 一个具有可选参数的对象,用于自定义Surface
的点击行为。 - 🔬
Filter
- 一个具有可选参数的对象,用于自定义Surface
的过滤器/效果。
🎊 一些额外的好东西
- 🔦
WithShading
-Color
扩展 - 🤚
DragNub
- 一个小的圆形“手柄”指示器,用于可视化可拖动材料的印象。
🌇 路线图
- 当前正在进行的更新可能会导致重大变化。
- 由于发布
Surface
,已经找到了更多高级和强大的包,但Surface
的开发仍然继续前进。 - 目前,只有
Surface
的形状变化的动画。
🌟 示例
查看 Surface
包的示例用法:
/// WORK IN PROGRESS
library surface_example;
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:surface/surface.dart';
import 'package:ball/ball.dart';
import 'surface_palette.dart';
import 'ball_pit.dart';
const _COLOR_PRIMARY = Colors.red;
const _COLOR_ACCENT = Colors.blue;
const _DURATION = Duration(milliseconds: 450);
const _BACKGROUND =
'https://apod.nasa.gov/apod/image/2102/rosette_goldman_2500.jpg';
void main() => runApp(SurfaceExample());
class SurfaceExample extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
// debugShowCheckedModeBanner: false,
title: 'Surface Example',
themeMode: ThemeMode.dark,
darkTheme: ThemeData.from(
colorScheme: ColorScheme.fromSwatch(
/// `ColorScheme.primary.withBlack(100)` 是 `Surface.baseColor` 的备用。
primarySwatch: _COLOR_PRIMARY,
brightness: Brightness.light,
accentColor: _COLOR_ACCENT,
/// Color 扩展 `.withBlack(int subtract)` 作为 Surface 包的一个额外好东西。
backgroundColor: _COLOR_PRIMARY.withBlack(150),
).copyWith(
/// `ColorScheme.surface` 是 `Surface.color` 的备用。
surface: _COLOR_ACCENT.withWhite(50).withOpacity(0.3),
),
).copyWith(
/// 🏓 [BouncyBall] 是 Surface 包中的另一个好东西。
splashFactory: BouncyBall.splashFactory,
// splashFactory: BouncyBall.splashFactory2,
// splashFactory: BouncyBall.splashFactory3,
// splashFactory: BouncyBall.splashFactory4,
// splashFactory: BouncyBall.marbleFactory,
// splashFactory: moldedBouncyBalls,
/// Surface `TapSpec.inkHighlightColor` 和 `inkSplashColor` 默认为 ThemeData。
splashColor: _COLOR_ACCENT,
highlightColor: _COLOR_PRIMARY.withOpacity(0.3),
),
home: const Landing(),
);
}
}
class SurfaceExampleDrawer extends StatelessWidget {
const SurfaceExampleDrawer({Key? key}) : super(key: key);
[@override](/user/override)
Widget build(BuildContext context) {
return Drawer(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildTile(context,
onSurface: '🎨\n'
'Surface\n'
'Palette',
newView: const SurfacePalette()),
buildTile(context,
onSurface: '🏓\n'
'Ball\n'
'Pit',
newView: const BallPit()),
],
),
);
}
Surface buildTile(
BuildContext context, {
required Widget newView,
required String onSurface,
}) {
return Surface(
width: 250.0,
height: 250.0,
padding: const EdgeInsets.all(25),
peek: const Peek(peek: 10),
shape: const Shape(
corners: CornerSpec.ROUNDED,
baseCorners: CornerSpec.SQUARED,
),
baseColor: _COLOR_ACCENT[700],
color: _COLOR_PRIMARY[900],
tapSpec: TapSpec(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => newView,
),
),
),
child: Text(
onSurface,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontSize: 50,
fontWeight: FontWeight.bold,
),
),
);
}
}
class Landing extends StatefulWidget {
const Landing({Key? key}) : super(key: key);
_LandingState createState() => _LandingState();
}
class _LandingState extends State<Landing> {
int _counter = 0;
late double _width, _height;
late Color _primary, _accent;
bool _isExampleBeveled = true,
_showExamplePopup = false,
_flipGradient = false;
late Timer appBarGradientTimer;
/// 因为这是一个示例应用...
void _incrementCounter() => setState(() => _counter++);
/// 重写 initState 并设置一个 Timer
[@override](/user/override)
void initState() {
super.initState();
/// 通过改变渐变来展示 Surface 的内在动画
appBarGradientTimer = Timer(
Duration(milliseconds: 2600),
() => setState(() => _flipGradient = true),
);
}
[@override](/user/override)
Widget build(BuildContext context) {
/// 存储每次构建 [_LandingState] 时的显示分辨率。
_width = MediaQuery.of(context).size.width;
_height = MediaQuery.of(context).size.height;
/// 并提供对 [Theme] 颜色的便捷访问。
_primary = Theme.of(context).primaryColor;
_accent = Theme.of(context).accentColor;
/// 这个基础 Surface 的颜色仅在 [_buildBackground] 加载背景图形之前可见。
return WillPopScope(
onWillPop: () async {
if (_showExamplePopup) {
setState(() => _showExamplePopup = false);
return false;
}
return true;
},
child: Surface(
shape: const Shape(
corners: CornerSpec.SQUARED,
),
peek: const Peek(peek: 0),
color: Theme.of(context).backgroundColor,
/// 因为 Surface 默认是 `TapSpec.tappable`,
/// 这两个 `Color` 参数将自定义长按 InkResponse 的外观...这些主题颜色在主 MaterialApp `ThemeData` 中初始化。
///
/// 由于这些主题颜色被 TapSpec 默认使用,
/// 我们将在未来的 TapSpecs 中省略它们的初始化。
///
/// 也可以参见主 ThemeData,其中 Surface 提供了额外的 [CustomInk.splashFactory] 用于墨水样式化。
tapSpec: TapSpec(
inkSplashColor: _accent,
inkHighlightColor: _primary.withOpacity(0.5),
),
/// 应用程序 Scaffold
child: Scaffold(
backgroundColor: Colors.transparent,
drawer: const SurfaceExampleDrawer(),
appBar: AppBar(
title: const Text('Surface Example'),
/// ➖ Surface 作为 AppBar
flexibleSpace: _surfaceAsAppBar(),
/// 这个按钮在 AppBar 中将切换一个布尔值,显示位于此堆栈上方的 [_surfaceAsPopup] (在 Z 轴上)。
actions: <Widget>[
IconButton(
icon: Icon((_showExamplePopup)
? Icons.close
: Icons.note_add_outlined),
onPressed: () =>
setState(() => _showExamplePopup = !_showExamplePopup),
)
],
),
/// Scaffold Body
body: Stack(
children: [
/// 🌆 背景图像
_buildBackground(),
/// 🔳 Surface 作为窗口
Stack(
children: [
Positioned(
top: _height * 0.075,
left: _width / 10,
child: _surfaceAsWindow(
context,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'A nice, basic counter example'.toUpperCase(),
style: Theme.of(context).textTheme.overline,
),
Text(
'Number of + Presses:',
style: Theme.of(context).textTheme.headline4,
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline1,
)
],
),
),
),
/// ✂ 状态控制按钮交换一些颜色并切换 [corners] 属性。
_stateControlButton(isShadow: true),
_stateControlButton(),
],
),
/// ❗ Surface 作为弹出窗口
/// 除非 [_showExamplePopup] == true,否则视觉上不显示。
Center(
child: _surfaceAsPopup(),
),
],
),
/// FAB
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
/// 🔘 Surface 作为浮动操作按钮
children: <Widget>[
_surfaceAsFAB(
filteredLayers: const {SurfaceLayer.MATERIAL},
passedString: 'filteredLayers:\nMATERIAL',
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
_surfaceAsFAB(
filteredLayers: Filter.BASE, // const {SurfaceLayer.BASE}
passedString: 'filteredLayers:\nBASE',
),
_surfaceAsFAB(
filteredLayers: Filter.BASE_AND_MATERIAL,
// filteredLayers: const {
// SurfaceLayer.BASE,
// SurfaceLayer.MATERIAL
// },
passedString: 'filteredLayers:\nBASE &\nMATERIAL',
),
],
),
],
),
),
),
);
}
/// 🌆 背景图像
Image _buildBackground() {
return Image.network(
_BACKGROUND,
// 这个 frameBuilder 简单地在加载时淡入照片。
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) return child;
return AnimatedOpacity(
child: child,
opacity: frame ==
null // 动画 gif 有多个帧,静态图片有 1 到加载,未加载的图片有 `null`
? 0
: 1,
duration: _DURATION * 2,
curve: _CURVE,
);
},
// 拉伸照片到应用程序的大小,并使其覆盖 Surface。
fit: BoxFit.cover,
width: _width,
height: _height,
);
}
/// ### ➖ Surface 作为 AppBar
Surface _surfaceAsAppBar() {
return Surface(
duration: _DURATION * 4,
curve: _CURVE,
width: _width,
height: double.infinity,
shape: const Shape(corners: CornerSpec.SQUARED),
/// 创建的 Timer 在几秒后倒计时,然后翻转此渐变以产生酷炫的效果。
gradient: LinearGradient(
begin: (_flipGradient) ? Alignment.centerRight : Alignment.centerLeft,
end: (_flipGradient) ? Alignment.centerLeft : Alignment.centerRight,
colors: [_accent, _primary],
stops: (_flipGradient) ? [0, 0.25] : [0, 0.75],
),
/// 确保边缘的边框非常薄,以不遮挡系统导航栏,但使用 `alignment` 和 `ratio` 给底部边缘一些厚度。
peek: const Peek(
peek: 1.5,
ratio: 2,
alignment: Alignment.bottomCenter,
),
/// 轻松地通过此渐变 `Alignment` 和 `baseGradient` 参数给系统导航栏顶部左边缘一个明亮的高光。
baseGradient: LinearGradient(
begin: const Alignment(-1, -1),
end: const Alignment(-0.97, 1),
colors: [
_primary.withWhite(100),
_primary,
_primary.withBlack(50),
],
),
);
}
/// ### 🔳 Surface 作为窗口
Surface _surfaceAsWindow(
BuildContext context, {
required Widget child,
}) {
return Surface(
child: child,
duration: _DURATION,
curve: _CURVE,
width: _width * 0.8,
height: _height * 0.75,
padding: const EdgeInsets.all(50),
shape: Shape(
// childScale: 0.75,
shapeScaleMaterial: 0.7,
// corners: (_isExampleBeveled)
// ? CornerSpec.BIBEVELED_50_FLIP
// : CornerSpec.CIRCLE,
corners: CornerSpec(
topLeft: (_isExampleBeveled) ? Corner.BEVEL : Corner.NONE,
topRight: (_isExampleBeveled) ? Corner.NONE : Corner.ROUND,
bottomRight: (_isExampleBeveled) ? Corner.SQUARE : Corner.BEVEL,
bottomLeft: (_isExampleBeveled) ? Corner.ROUND : Corner.SQUARE,
radius: BorderRadius.all(Radius.circular(35)),
),
// baseCorners: CornerSpec(
// topLeft: (_isExampleBeveled) ? Corner.BEVEL : Corner.SQUARE,
// topRight: Corner.NONE,
// radius: BorderRadius.all(Radius.circular(165)),
// ),
),
peek: Peek(
peek: 20,
ratio: (_isExampleBeveled) ? 2.5 : 5,
alignment:
(_isExampleBeveled) ? Alignment.bottomRight : Alignment.topCenter,
),
tapSpec: const TapSpec(
// tappable: false,
inkSplashColor: Colors.deepPurpleAccent,
),
filter: Filter(
// filteredLayers: FilterSpec.TRILAYER,
filteredLayers: Filter.NONE, // 覆盖 `radii` 下面
radiusBase: 1.0,
radiusMaterial: 2.0,
// radiusChild: 20.0,
/// `filteredLayers: FilterSpec.NONE` 上面所以 `specRadius` == 0,
/// 但是 `SurfaceLayer` 仍然交付。
effect: (double specRadius, SurfaceLayer layerForRender) =>
/// 覆盖上面的 `filteredLayers` 和 `radii`
// FX.b(specRadius), // 但是当 `FilterSpec.NONE` -> `specRadius` == 0,所以
FX.b(layerForRender == SurfaceLayer.CHILD ? specRadius : 2.5),
),
baseColor: Colors.black38,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: (_isExampleBeveled)
? [
_primary.withWhite(50).withOpacity(0.5),
_primary.withBlack(50).withOpacity(0.5)
]
: [
_accent.withWhite(50).withOpacity(0.5),
_accent.withBlack(50).withOpacity(0.5)
],
),
);
}
/// ### ✂ 状态控制按钮
Widget _stateControlButton({bool isShadow = false}) {
double top = (_isExampleBeveled) ? _height * 0.1 : (_height * 0.1) / 2;
double left = (_isExampleBeveled) ? _width / 7 : (_width / 7) / 3;
Color color = (_isExampleBeveled) ? _accent : _primary;
return AnimatedPositioned(
duration: _DURATION * 4,
curve: Curves.elasticOut,
top: (isShadow) ? top + 1 : top,
left: (isShadow) ? left + 1 : left,
/// 这个按钮将控制我们的主要中央 [surfaceAsWindow] 的状态。
///
/// `bool _isExampleBeveled` 在整个构建中用于控制外观。
child: IconButton(
icon: Icon(
(_isExampleBeveled) ? Icons.add_box_rounded : Icons.cut_sharp,
),
color: (isShadow) ? color.withBlack(75) : color,
iconSize: 50,
onPressed: () => setState(() => _isExampleBeveled = !_isExampleBeveled),
),
);
}
/// 🔘 Surface 作为 FAB
Surface _surfaceAsFAB({
required Set<SurfaceLayer> filteredLayers,
required String passedString,
}) {
return Surface(
/// `surfaceAsPopup` 是一个叠加窗口,但如果未考虑 `_showExamplePopup`,
/// 则 FABs 仍然会在其上方。
width: (_showExamplePopup) ? 0 : 175,
height: (_showExamplePopup) ? 0 : 175,
padding: const EdgeInsets.all(10),
// padLayer: SurfaceLayer.MATERIAL, // 默认为 [SurfacePadding.PAD_CHILD]。
duration: _DURATION,
peek: const Peek(
peek: 30,
// alignment: Alignment.bottomCenter,
// ratio: 1.25,
),
shape: const Shape(
corners: CornerSpec.CIRCLE,
baseBorder: BorderSide(color: Colors.black38, width: 5.0),
border: BorderSide.none,
),
/// 透明颜色允许在这些示例情况下纯粹看到模糊效果。
color: Colors.transparent,
// color: Colors.white12,
/// 使用 [_stateControlButton] 时的有趣颜色交换。
baseColor: (_isExampleBeveled)
? _accent.withWhite(25).withOpacity(0.25)
: _primary.withWhite(25).withOpacity(0.25),
filter: Filter(
filteredLayers: filteredLayers,
// 声明 `radiusMap` 就像显式声明这些双精度值一样:
// baseRadius: 3.0,
// materialRadius: 15.0,
radiusMap: const {
SurfaceLayer.BASE: 3.0,
SurfaceLayer.MATERIAL: 15.0,
},
),
/// 强制性计数器示例实现;
tapSpec: TapSpec(
// tappable: false, // `true` by default
providesFeedback: true, // `false` by default
onTap: _incrementCounter,
),
/// 加号图标和标签
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Flexible(child: Icon(Icons.add, color: Colors.white)),
Flexible(
child: Text(
passedString,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 12, color: Colors.white),
),
)
],
),
);
}
/// ❗ Surface 作为弹出窗口
Surface _surfaceAsPopup() {
return Surface(
/// 覆盖大部分屏幕
width: (_showExamplePopup) ? _width - 50 : 0,
height: (_showExamplePopup) ? _height / 2 : 0,
padding: const EdgeInsets.all(50),
shape: Shape(
// childScale: 1.0,
// materialScale: 1.0,
// childScale: 0.5,
// materialScale: 0.8,
padLayer: SurfaceLayer.MATERIAL, // 区分的层用于 Filter
corners: CornerSpec.beveledWith(
topLeft: Corner.SQUARE,
topRight: Corner.ROUND,
bottomRight: Corner.ROUND,
radius: BorderRadius.vertical(
bottom: Radius.elliptical(200, 40),
top: Radius.elliptical(80, 200),
),
),
),
duration: _DURATION,
curve: _CURVE,
/// 从 Material 原色中随机选择颜色
color: Colors.primaries[Random().nextInt(Colors.accents.length)]
.withBlack(75)
.withOpacity(0.4),
baseColor: Colors.black38, // 默认为 `ColorScheme.primaryVariant`
/// 当 [_surfaceAsPopup] 隐藏 `!(_showExamplePopup)` 时,给一个更厚的边框,
/// 结果是在进入动画期间的一个整洁的扩展。
peek: Peek(
// peek: 0,
peek: (_showExamplePopup) ? 25 : 30,
ratio: (_showExamplePopup) ? 4 : 7,
alignment: Alignment.topLeft,
),
tapSpec: TapSpec(
/// 此处的 onTap 将刷新构建并给出新的随机颜色
onTap: () => setState(() {}),
providesFeedback: true,
),
// Child 和 Material filters 占据相同的空间,除非
// `ShapeSpec(padLayer: SurfaceLayer.MATERIAL)`
filter: const Filter(
// filteredLayers: Filter.TRILAYER,
filteredLayers: Filter.BASE_AND_MATERIAL,
radiusMap: {
SurfaceLayer.BASE: 3.0,
SurfaceLayer.MATERIAL: 4.0,
// SurfaceLayer.CHILD: 20.0
},
),
/// [_surfaceAsPopup] 的内容
child: Container(
padding: const EdgeInsets.all(20),
color: Colors.black12,
alignment: Alignment.center,
child: const FittedBox(
/// 使用 FittedBox,可以自由使用巨大的 fontSize。
child: Text(
'p o p u p',
style: TextStyle(color: Colors.white, fontSize: 100),
),
),
),
);
}
}
更多关于Flutter未知功能插件surface的介绍与使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter未知功能插件surface的介绍与使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
Flutter未知功能插件Surface的介绍与使用
在Flutter开发中,插件(Plugins)是连接Flutter代码与原生平台(如Android和iOS)功能的重要桥梁。尽管Flutter官方库和社区已经提供了大量的插件来满足常见的开发需求,但有时候开发者可能会遇到一些特定的、未被广泛认知的“未知功能”需求,这时候就需要创建或使用自定义插件。
surface
这个词在Flutter官方文档和插件库中并不是一个具体的插件名称,但我们可以将其理解为一个泛指,代表那些可能涉及到底层渲染、图形处理、或者与原生平台深度交互的自定义插件。下面我将介绍如何创建并使用一个假设的“Surface”插件,这个插件可能用于在Flutter应用中展示一些自定义渲染的内容。
1. 创建插件项目
首先,使用Flutter的插件生成工具创建一个新的插件项目。假设我们命名为surface_plugin
。
flutter create --org com.example --template=plugin surface_plugin
2. 实现Android平台代码
在surface_plugin/android/src/main/kotlin/com/example/surface_plugin
目录下,创建一个SurfaceView.kt
文件,用于实现自定义的SurfaceView。
package com.example.surface_plugin
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.SurfaceHolder
import android.view.SurfaceView
class CustomSurfaceView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback {
private val paint: Paint = Paint()
init {
holder.addCallback(this)
}
override fun surfaceCreated(holder: SurfaceHolder) {
// 在这里开始绘制
val canvas = holder.lockCanvas()
if (canvas != null) {
draw(canvas)
holder.unlockCanvasAndPost(canvas)
}
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
// 处理Surface变化
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
// 清理资源
}
private fun draw(canvas: Canvas) {
// 简单的绘制示例
paint.color = android.graphics.Color.RED
canvas.drawRect(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat(), paint)
}
}
然后在SurfacePlugin.kt
中注册这个自定义View。
package com.example.surface_plugin
import android.content.Context
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodChannel
class SurfacePlugin: FlutterPlugin, ActivityAware {
private var channel: MethodChannel? = null
private var context: Context? = null
override fun onAttachedToEngine(@NonNull flutterEngine: FlutterEngine) {
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "surface_plugin")
context = flutterEngine.context
channel?.setMethodCallHandler { call, result ->
if (call.method == "showSurface") {
// 在这里你可以创建并显示CustomSurfaceView
// 例如,你可以通过某种方式将View添加到当前的Activity或Fragment中
result.success(null)
} else {
result.notImplemented()
}
}
}
override fun onDetachedFromEngine(@NonNull flutterEngine: FlutterEngine) {
channel?.setMethodCallHandler(null)
channel = null
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
// 可以在这里保存Activity引用,用于后续操作
}
override fun onDetachedFromActivityForConfigChanges() {
// 配置变化时处理
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
// 配置恢复时处理
}
override fun onDetachedFromActivity() {
// Activity销毁时处理
}
}
3. 实现iOS平台代码
由于iOS与Android的差异,你可能需要实现一个自定义的UIView
或UIViewController
来替代Android中的SurfaceView
。这里仅提供一个简单的框架,具体实现取决于你的需求。
在surface_plugin/ios/Classes/SurfacePlugin.swift
中:
import Flutter
import UIKit
public class SurfacePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "surface_plugin", binaryMessenger: registrar.messenger())
let instance = SurfacePlugin()
instance.setup(channel: channel, registrar: registrar)
}
private var channel: FlutterMethodChannel?
private func setup(channel: FlutterMethodChannel, registrar: FlutterPluginRegistrar) {
self.channel = channel
channel?.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
switch call.method {
case "showSurface":
// 在这里创建并展示自定义的UIView或UIViewController
result(nil)
default:
result(.notImplemented)
}
})
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
// 其他方法调用处理
}
}
4. 在Flutter中使用插件
在Flutter项目中,添加对surface_plugin
的依赖,并在Dart代码中调用。
dependencies:
surface_plugin:
path: ../path_to_your_plugin
然后在Dart代码中:
import 'package:flutter/material.dart';
import 'package:surface_plugin/surface_plugin.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Surface Plugin Demo'),
),
body: Center(
child: ElevatedButton(
onPressed: _showSurface,
child: Text('Show Surface'),
),
),
),
);
}
void _showSurface() async {
try {
await SurfacePlugin.showSurface();
} catch (e) {
print(e);
}
}
}
请注意,以上代码是一个简化的示例,旨在展示如何开始创建和使用一个自定义的Flutter插件。在实际项目中,你可能需要处理更多的细节,比如生命周期管理、错误处理、以及更复杂的原生代码实现。