2024年最全Flutter学习之视图体系,体系面试问题

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节

还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一线互联网面试专题

379页的Android进阶知识大全

379页的Android进阶知识大全

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

VoidCallback get onMetricsChanged => _onMetricsChanged;
VoidCallback _onMetricsChanged;
Zone _onMetricsChangedZone;
set onMetricsChanged(VoidCallback callback) {
_onMetricsChanged = callback;
_onMetricsChangedZone = Zone.current;
}

//当系统语言发生变化时触发回调
Locale get locale {
if (_locales != null && _locales.isNotEmpty) {
return _locales.first;
}
return null;
}

//获取系统语言
List get locales => _locales;
List _locales;

//当Local发生改变时触发回调
VoidCallback get onLocaleChanged => _onLocaleChanged;
VoidCallback _onLocaleChanged;
Zone _onLocaleChangedZone;
set onLocaleChanged(VoidCallback callback) {
_onLocaleChanged = callback;
_onLocaleChangedZone = Zone.current;
}

//当系统字体大小改变时触发回调
VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;
VoidCallback _onTextScaleFactorChanged;
Zone _onTextScaleFactorChangedZone;
set onTextScaleFactorChanged(VoidCallback callback) {
_onTextScaleFactorChanged = callback;
_onTextScaleFactorChangedZone = Zone.current;
}

//屏幕亮度改变时触发回调
VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged;
VoidCallback _onPlatformBrightnessChanged;
Zone _onPlatformBrightnessChangedZone;
set onPlatformBrightnessChanged(VoidCallback callback) {
_onPlatformBrightnessChanged = callback;
_onPlatformBrightnessChangedZone = Zone.current;
}

//屏幕刷新时会回调
FrameCallback get onBeginFrame => _onBeginFrame;
FrameCallback _onBeginFrame;
Zone _onBeginFrameZone;
set onBeginFrame(FrameCallback callback) {
_onBeginFrame = callback;
_onBeginFrameZone = Zone.current;
}

//绘制屏幕时回调
VoidCallback get onDrawFrame => _onDrawFrame;
VoidCallback _onDrawFrame;
Zone _onDrawFrameZone;
set onDrawFrame(VoidCallback callback) {
_onDrawFrame = callback;
_onDrawFrameZone = Zone.current;
}

//点击或者指针事件触发回调
PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;
PointerDataPacketCallback _onPointerDataPacket;
Zone _onPointerDataPacketZone;
set onPointerDataPacket(PointerDataPacketCallback callback) {
_onPointerDataPacket = callback;
_onPointerDataPacketZone = Zone.current;
}

//获取请求的默认路由
String get defaultRouteName => _defaultRouteName();
String _defaultRouteName() native ‘Window_defaultRouteName’;

//该方法被调用后,onBeginFrame和onDrawFrame将紧连着会在合适时机调用
void scheduleFrame() native ‘Window_scheduleFrame’;

//更新应用在GPU上的渲染,这方法会直接调用Flutter engine的Window_render方法
void render(Scene scene) native ‘Window_render’;

//窗口的语义内容是否改变
bool get semanticsEnabled => _semanticsEnabled;
bool _semanticsEnabled = false;

//当窗口语言发生改变时回调
VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged;
VoidCallback _onSemanticsEnabledChanged;
Zone _onSemanticsEnabledChangedZone;
set onSemanticsEnabledChanged(VoidCallback callback) {
_onSemanticsEnabledChanged = callback;
_onSemanticsEnabledChangedZone = Zone.current;
}

//当用户表达写的动作时回调
SemanticsActionCallback get onSemanticsAction => _onSemanticsAction;
SemanticsActionCallback _onSemanticsAction;
Zone _onSemanticsActionZone;
set onSemanticsAction(SemanticsActionCallback callback) {
_onSemanticsAction = callback;
_onSemanticsActionZone = Zone.current;
}

//启用其他辅助功能回调
VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged;
VoidCallback _onAccessibilityFeaturesChanged;
Zone _onAccessibilityFlagsChangedZone;
set onAccessibilityFeaturesChanged(VoidCallback callback) {
_onAccessibilityFeaturesChanged = callback;
_onAccessibilityFlagsChangedZone = Zone.current;
}

//更新此窗口的语义数据
void updateSemantics(SemanticsUpdate update) native ‘Window_updateSemantics’;

//设置Isolate调试名称
void setIsolateDebugName(String name) native ‘Window_setIsolateDebugName’;

//向特定平台发送消息
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) {
final String error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null)
throw new Exception(error);
}
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native ‘Window_sendPlatformMessage’;

//获取平台消息的回调
PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;
PlatformMessageCallback _onPlatformMessage;
Zone _onPlatformMessageZone;
set onPlatformMessage(PlatformMessageCallback callback) {
_onPlatformMessage = callback;
_onPlatformMessageZone = Zone.current;
}

//由_dispatchPlatformMessage调用
void _respondToPlatformMessage(int responseId, ByteData data)
native ‘Window_respondToPlatformMessage’;

//平台信息响应回调
static PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback callback) {
if (callback == null)
return null;

// Store the zone in which the callback is being registered.
final Zone registrationZone = Zone.current;

return (ByteData data) {
registrationZone.runUnaryGuarded(callback, data);
};
}
}

可以知道window包含当前设备系统的一些信息和回调,那么现在看看混入的各种Binding:

  • GestureBinding:绑定手势子系统,提供onPointerDataPacket回调。
  • ServicesBinding:绑定平台消息通道,提供onPlatformMessage回调。
  • SchedulerBinding:绑定绘制调度子系统,提供onBeginFrameonDrawFrame回调。
  • PaintingBinding:绑定绘制库,处理图像缓存。
  • SemanticsBinding:语义层和flutter engine的桥梁,对辅助功能的底层支持。
  • RendererBinding:是渲染树和Flutter engine的桥梁,提供onMetricsChangedonTextScaleFactorChanged回调。
  • WidgetsBinding:是Flutter widget和engine的桥梁,提供onLocaleChangedonBuildSchedule回调。

也就是WidgetsFlutterBinding.ensureInitialized()这行代码看名字是将WidgetsFlutterBinding实例初始化。其实并非那么简单,另外获取一些系统基本信息和初始化监听window对象的一些事件,然后将这些事件按照上层Framework模型规则进行包装、抽象最后分发。简而言之WidgetsFlutterBindingFlutter engineFramework的桥梁。

2.attachRootWidget(app)

第二行是attachRootWidget(app),看名字意思是将根RootWidget挂载。点进去看:

//如果这个widget有必要创建,就将它附加到[renderViewElement]
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: ‘[root]’,
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement);
}

renderView是UI渲染树的根节点:

// The render tree that’s attached to the output surface.
//挂载在渲染树 rootNode根节点
RenderView get renderView => _pipelineOwner.rootNode;

rootWidget是外面所传进来的根Widget,这里不用管它,下面看attachToRenderTree(buildOwner, renderViewElement)这个方法,根据意思,这个方法应该构建RenderTree,这个方法需要两个参数,首先看buildOwner这个参数:

/// The [BuildOwner] in charge of executing the build pipeline for the
/// widget tree rooted at this binding.
BuildOwner get buildOwner => _buildOwner;
final BuildOwner _buildOwner = BuildOwner();

看官网对它的定义:

image.png

意思是:是widget framework的管理类,用来跟踪哪些widget需要重建,并处理widget树的其他任务,例如管理树的非活动元素列表,并在调试时在热重载期间在必要时触发“重组”命令,下面看另外一个参数renderViewElement,代码注释如下:

/// The [Element] that is at the root of the hierarchy (and which wraps the
/// [RenderView] object at the root of the rendering hierarchy).
///
/// This is initialized the first time [runApp] is called.
Element get renderViewElement => _renderViewElement;
Element _renderViewElement;

renderViewElementrenderView对应的Element对象,因为renderView是树的根,所以renderViewElement位于层次结构的根部,那下面点击attachToRenderTree的源码看:

/// Inflate this widget and actually set the resulting [RenderObject] as the
/// child of [container].
///
/// If element is null, this function will create a new element. Otherwise,
/// the given element will have an update scheduled to switch to this widget.
///
/// Used by [runApp] to bootstrap applications.
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}

跟着代码往下走如果根element没有创建,那么就调用createElement创建根elementelementwidget进行关联。接着调用element.assignOwner(owner),这个方法其实就是设置这个element的跟踪,最后调用owner.buildScope这个方法,这个方法是确定更新widget的范围。如果element已经创建了,将根element和关联的widget设为新的,并且重新构建这个element,为了后面的复用。

3.scheduleWarmUpFrame

runApp()方法最后一行执行scheduleWarmUpFrame方法:

/// Schedule a frame to run as soon as possible, rather than waiting for
/// the engine to request a frame in response to a system “Vsync” signal.
///
/// This is used during application startup so that the first frame (which is
/// likely to be quite expensive) gets a few extra milliseconds to run.
///
/// Locks events dispatching until the scheduled frame has completed.
///
/// If a frame has already been scheduled with [scheduleFrame] or
/// [scheduleForcedFrame], this call may delay that frame.
///
/// If any scheduled frame has already begun or if another
/// [scheduleWarmUpFrame] was already called, this call will be ignored.
///
/// Prefer [scheduleFrame] to update the display in normal operation.
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;

_warmUpFrame = true;
Timeline.startSync(‘Warm-up frame’);
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null);—>1
});
Timer.run(() {
assert(_warmUpFrame);
handleDrawFrame(); ---->2
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame’s timestamp will typically be far in
// the past (the time of the last real frame), so if we didn’t reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the “real” frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
});

// Lock events so touch events etc don’t insert themselves until the
// scheduled frame has finished.
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}

首先这个方法在SchedulerBinding里,这两个方法主要执行了handleBeginFramehandleDrawFrame方法:

3.1.handleBeginFrame

void handleBeginFrame(Duration rawTimeStamp) {

try {
// TRANSIENT FRAME CALLBACKS
Timeline.startSync(‘Animate’, arguments: timelineWhitelistArguments);
_schedulerPhase = SchedulerPhase.transientCallbacks;
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
_transientCallbacks = <int, _FrameCallbackEntry>{};
callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
if (!_removedIds.contains(id))
_invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp, callbackEntry.debugStack);
});
_removedIds.clear();
} finally {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}

可以看到主要是对transientCallbacks队列操作,这个集合主要是放一些临时回调,存放动画回调。

3.2.handleDrawFrame

void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the “Animate” phase
try {
// PERSISTENT FRAME CALLBACKS ----->
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);

// POST-FRAME CALLBACKS ------>
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List localPostFrameCallbacks =
List.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
_schedulerPhase = SchedulerPhase.idle;
Timeline.finishSync(); // end the Frame
profile(() {
_profileFrameStopwatch.stop();
_profileFramePostEvent();
});
assert(() {
if (debugPrintEndFrameBanner)
debugPrint(‘▀’ * _debugBanner.length);
_debugBanner = null;
return true;
}());
_currentFrameTimeStamp = null;
}
}

可以看到执行persistentCallbacks队列,这个队列用于存放一些持久的回调,不能再此类回调中在请求新的绘制帧,持久回调一经注册则不能移除。接着执行postFrameCallbacks这个队列在每一Frame(一次绘制)结束时只会调用一次,调用后被系统移除。

也就是scheduleWarmUpFrame这个方法安排帧尽快执行,当一次帧绘制结束之前不会响应各种事件,这样保证绘制过程中不触发重绘。上面说过:

RendererBinding:是渲染树和Flutter engine的桥梁,提供onMetricsChanged和onTextScaleFactorChanged回调

Flutter真正渲染和绘制是在这个绑定里:

4.渲染绘制

void initInstances() {
super.initInstances();
_instance = this;//初始化
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
//添加设置监听
window
…onMetricsChanged = handleMetricsChanged
…onTextScaleFactorChanged = handleTextScaleFactorChanged
…onPlatformBrightnessChanged = handlePlatformBrightnessChanged
…onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
…onSemanticsAction = _handleSemanticsAction;
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
//添加persistentFrameCallback
addPersistentFrameCallback(_handlePersistentFrameCallback);
//创建触摸管理
_mouseTracker = _createMouseTracker();
}

addPersistentFrameCallback这个方法主要向persistentFrameCallback添加了回调:

void addPersistentFrameCallback(FrameCallback callback) {
_persistentCallbacks.add(callback);
}

再看_handlePersistentFrameCallback这个回调做了什么:

void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
}

@protected
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();//更新布局信息
pipelineOwner.flushCompositingBits();//在flushLayout只后调用,在flushPaint之前调用,更新RenderObject是否需要重绘
pipelineOwner.flushPaint();//更新绘制RenderObject
renderView.compositeFrame(); // 发送bit数据给GPU
pipelineOwner.flushSemantics(); // 发送语义数据给操作系统
}

下面一个一个方法走:

4.1.flushLayout

void flushLayout() {
profile(() {
Timeline.startSync(‘Layout’, arguments: timelineWhitelistArguments);
});
assert(() {
_debugDoingLayout = true;
return true;
}());
try {
// TODO(ianh): assert that we’re not allowing previously dirty nodes to redirty themselves
while (_nodesNeedingLayout.isNotEmpty) {
final List dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = [];
for (RenderObject node in dirtyNodes…sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
if (node._needsLayout && node.owner == this)
node._layoutWithoutResize();
}
}
} finally {
assert(() {
_debugDoingLayout = false;
return true;
}());
profile(() {
Timeline.finishSync();
});
}
}

看源码得知首先获取哪些标记为RenderObject的布局信息,然后通过ode._layoutWithoutResize();重新调整这些RenderObject

4.2.flushCompositingBits

void flushCompositingBits() {
profile(() { Timeline.startSync(‘Compositing bits’); });
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate && node.owner == this)
node._updateCompositingBits();
}
_nodesNeedingCompositingBitsUpdate.clear();
profile(() { Timeline.finishSync(); });
}

检查RenderObject是否需要重绘,并且通过node._updateCompositingBits();更新_needsCompositing这个属性,若为true就要重新绘制,否则不需要。

4.3.flushPaint

void flushPaint() {
profile(() { Timeline.startSync(‘Paint’, arguments: timelineWhitelistArguments); });
assert(() {
_debugDoingPaint = true;
return true;
}());
try {
final List dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = [];
// Sort the dirty nodes in reverse order (deepest first).
//方向遍历这些标记过的node
for (RenderObject node in dirtyNodes…sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
assert(node._layer != null);
if (node._needsPaint && node.owner == this) {
if (node._layer.attached) {
//重新绘制
PaintingContext.repaintCompositedChild(node);
} else {
node._skippedPaintingOnLayer();
}
}
}
assert(_nodesNeedingPaint.isEmpty);
} finally {
assert(() {
_debugDoingPaint = false;
return true;
}());
profile(() { Timeline.finishSync(); });
}
}

这个方法通过反向遍历(dirty标记)取得需要重绘的RenderObject,最后通过PaintingContext.repaintCompositedChild(node);重绘。

4.4.compositeFrame

void compositeFrame() {
Timeline.startSync(‘Compositing’, arguments: timelineWhitelistArguments);
try {
//创建Scene对象
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene scene = layer.buildScene(builder);
if (automaticSystemUiAdjustment)
_updateSystemChrome();
//使用render方法将Scene对象显示在屏幕上
_window.render(scene);//调用flutter engine的渲染API
scene.dispose();
assert(() {
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);
return true;
}());
} finally {
Timeline.finishSync();
}
}

Scene是用来保存渲染后的最终像素信息,这个方法将Canvas画好的Scene对象传给window.render()方法,该方法会直接将Scene信息发送给Flutter engine,最终Flutter engine将图像画在设备屏幕上,这样整个绘制流程就算完了。

注意:RendererBinding只是混入对象,最终混入到WidgetsBinding,回到最开始来看:

class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

/// Returns an instance of the [WidgetsBinding], creating and
/// initializing it if necessary. If one is created, it will be a
/// [WidgetsFlutterBinding]. If one was previously initialized, then
/// it will at least implement [WidgetsBinding].
///
/// You only need to call this method if you need the binding to be
/// initialized before calling [runApp].
///
/// In the flutter_test framework, [testWidgets] initializes the
/// binding instance to a [TestWidgetsFlutterBinding], not a
/// [WidgetsFlutterBinding].
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}

所以应该WidgetsBinding来重写实现drawFrame方法:

@override
void drawFrame() {
assert(!debugBuildingDirtyElements);
assert(() {
debugBuildingDirtyElements = true;
return true;
}());
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame(); //调用Renderbinding的drawFrame方法
buildOwner.finalizeTree();
} finally {
assert(() {
debugBuildingDirtyElements = false;
return true;
}());
}
profile(() {
if (_needToReportFirstFrame && _reportFirstFrame) {
developer.Timeline.instantSync(‘Widgets completed first useful frame’);
developer.postEvent(‘Flutter.FirstFrame’, <String, dynamic>{});
_needToReportFirstFrame = false;
}
});
}

最后

跳槽季整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ent(‘Flutter.FirstFrame’, <String, dynamic>{});
_needToReportFirstFrame = false;
}
});
}

最后

跳槽季整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-PAjzcapZ-1715887353734)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter是一种跨平台的移动应用开发框架,可以使用Dart语言编写代码,并在Android和iOS等多个平台上运行。在Flutter中,可以使用原生视图进行开发,以便实现更高级的功能或满足特定的需求。 拷贝原生视图是指将原生平台上的视图组件(如Android上的View或iOS上的UIView)复制到Flutter应用中使用。这样做的好处是可以充分利用原生平台的特性和功能,同时保持Flutter应用的跨平台性。 在Flutter中进行原生视图的拷贝,通常需要使用Flutter提供的插件或包来实现。这些插件或包可以帮助开发者将原生视图嵌入到Flutter应用中,并提供访问原生平台的接口。 具体的操作步骤可能会因为不同平台和插件的差异而有所不同,但一般来说,大致可以分为以下几个步骤: 1. 导入插件或包:根据所需的功能和平台选择合适的插件或包,并在Flutter项目中导入。 2. 配置插件或包:根据需要,配置插件或包,并在Flutter项目的主配置文件中进行必要的设置。 3. 创建原生视图:使用插件或包提供的方法,在Flutter应用的代码中创建原生视图对应的组件,并指定需要传递给原生平台的参数和回调函数。 4. 添加原生视图:将创建的原生视图组件添加到Flutter应用的界面中的适当位置,以便显示和使用。 5. 处理交互:根据需要,实现与原生视图的交互,包括接收原生平台传递的事件和数据,并根据业务逻辑进行处理。 需要注意的是,在使用原生视图的同时,还需要兼顾Flutter框架的特性和约束,确保原生视图Flutter应用其他部分的协调和一致性。 通过拷贝原生视图,我们可以在Flutter应用中实现更复杂、更灵活的功能,同时利用Flutter框架的优势,实现跨平台的移动应用开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值