2024 Flutter面試題一

Dart部分

String擴(kuò)展一個(gè)方法
  1. 使用關(guān)鍵字extension ... on為String定義一個(gè)擴(kuò)展類
  2. 在為擴(kuò)展類添加一個(gè) 新的方法
  3. String類型對(duì)象調(diào)用這個(gè)擴(kuò)展的方法
extension StringExt on String{
  // 2. 擴(kuò)展方法
  int add(int x, int y){
    return x + y;
  }
}

void mian(){
  String str = 'hello';
  // 3. 使用擴(kuò)展類方法
  int result = str.add(3, 7);
  debugPrint(result.toString());
}

// 單元測(cè)試
import 'package:flutter_start/extTest.dart';
import 'package:flutter_test/flutter_test.dart';
void main(){
  test('StringExt', (){
    String ext = 'ext';
    expect(ext.add(3, 7), 10);
  });
}
image.png
dart是單繼承還是多繼承蛾方?

單繼承

dart如何達(dá)到多繼承的效果转捕?

Dart中使用Mixins,可以達(dá)到多繼承的效果

mixin混入有什么特點(diǎn)
  1. 作為mixins的類只能繼承自O(shè)bject痘儡,不能繼承其他類
  2. 作為mixins的類不能有構(gòu)造函數(shù)
  3. 一個(gè)類可以mixins多個(gè)mixins類
  4. mixins絕不是繼承沉删,也不是接口醉途,而是一種全新的特性
// 類D 繼承A和B   關(guān)鍵字 with
class D  extends A with B{

}

// mixin 的使用
class A {
  String info="this is A";
  void printA(){
    print("A");
  }
  void run(){
    print("A Run");
  }
}
class B {  
  void printB(){
    print("B");
  }
  void run(){
    print("B Run");
  }
}
class C extends Person with B,A{
  C(String name, num age) : super(name, age);
}
混入相同方法的多個(gè)混入隘擎,最終會(huì)執(zhí)行哪一個(gè)货葬?

后面的類中的方法將前面的類中相同的方法覆蓋

dart運(yùn)行機(jī)制是什么樣的?

消息循環(huán)機(jī)制

  1. 兩個(gè)隊(duì)列休傍,微任務(wù)隊(duì)列和事件隊(duì)列蹲姐。
  2. microtask queue 的優(yōu)先級(jí)高于event queue柴墩。
  3. 在每一次事件循環(huán)中江咳,Dart總是先去第一個(gè)microtask queue中查詢是否有可執(zhí)行的任務(wù),如果沒有汹胃,才會(huì)處理后續(xù)的event queue的流程着饥。
如何向事件隊(duì)列插入任務(wù)惰赋?

Future就是將任務(wù)插入到事件隊(duì)列

向微任務(wù)隊(duì)列插入任務(wù)

Future.microtask()
scheduleMicrotask()
Stream中的執(zhí)行異步的模式就是scheduleMicrotask赁濒。因?yàn)閙icrotask的優(yōu)先級(jí)又高于event。所以挪拟,如果 microtask 太多就可能會(huì)對(duì)觸摸玉组、繪制等外部事件造成阻塞卡頓丁侄。

Future和Stream有什么區(qū)別鸿摇?
  1. Future中的任務(wù)會(huì)加入下一輪事件循環(huán)拙吉,而Stream中的任務(wù)則是加入微任務(wù)隊(duì)列
  2. Future 用于表示單個(gè)運(yùn)算的結(jié)果,而 Stream 則表示多個(gè)結(jié)果的序列恩商。

Stream 有同步流和異步流之分怠堪。它們的區(qū)別在于同步流會(huì)在執(zhí)行 add名眉,addError 或 close 方法時(shí)立即向流的監(jiān)聽器 StreamSubscription 發(fā)送事件损拢,而異步流總是在事件隊(duì)列中的代碼執(zhí)行完成后在發(fā)送事件。`

Stream訂閱模式有哪幾種掏秩?

Stream分為Single Subscription和Broadcast兩種類型蒙幻, 前者只允許訂閱(listen)一次邮破,后者允許多次訂閱。

單訂閱在訂閱者出現(xiàn)之前會(huì)持有數(shù)據(jù)矫渔,在訂閱者出現(xiàn)之后就才轉(zhuǎn)交給它庙洼。
廣播訂閱可以同時(shí)有多個(gè)訂閱者范嘱,當(dāng)有數(shù)據(jù)時(shí)就會(huì)傳遞給所有的訂閱者丑蛤,而不管當(dāng)前是否已有訂閱者存在受裹。

Stream單訂閱,多次訂閱會(huì)出現(xiàn)什么結(jié)果厦章?

會(huì)報(bào)錯(cuò)照藻,單訂閱只能有一次訂閱.
即使取消了第一個(gè)監(jiān)聽器幸缕,也不允許在單訂閱流上設(shè)置其他的監(jiān)聽器发乔。

Stream 可以通過 transform() 方法(返回另一個(gè) Stream)進(jìn)行連續(xù)調(diào)用。
通過 Stream.asBroadcastStream() 可以將一個(gè)單訂閱模式的 Stream 轉(zhuǎn)換成一個(gè)多訂閱模式的 Stream起愈,isBroadcast 屬性可以判斷當(dāng)前 Stream 所處的模式抬虽。

dart是單線程還是多線程?

Dart是單線程模型缰猴。

Dart是如何實(shí)現(xiàn)多任務(wù)并行的

主要依賴dart的并發(fā)編程(Isolate)、異步和事件驅(qū)動(dòng)機(jī)制

Completer
// 獲取圖片的寬高
  Future<Size> getImageSize(String path) {
    Completer<Size> completer =  Completer<Size>();
    Image image = Image.file(File.fromUri(Uri.parse(path)));
    // 預(yù)先獲取圖片信息
    image.image.resolve(const ImageConfiguration()).addListener(
        ImageStreamListener((ImageInfo info, bool _) {
          Size size = Size(info.image.width.toDouble(), info.image.height.toDouble());
          completer.complete(size);
        }));
    return completer.future;
  } 
Isolate
  • 使用場(chǎng)景:視頻的編碼轉(zhuǎn)碼隘膘,需要非常高的CPU計(jì)算
  • 在dart中弯菊,一個(gè)Isolate對(duì)象其實(shí)就是一個(gè)Isolate執(zhí)行環(huán)境的引用管钳,一般來說我們都是通過當(dāng)前的Isolate去控制其他的Isolate完成彼此之間的交互软舌,而當(dāng)我們想要?jiǎng)?chuàng)建一個(gè)新的Isolate可以使用Isolate.spawn方法獲取一個(gè)新的Isolate對(duì)象佛点,兩個(gè)Isolate之間使用SendPort相互發(fā)送消息超营,而Isolate中也存在了一個(gè)與之對(duì)應(yīng)ReceivePort接收消息用來處理,但是我們需要注意的是SendPortReceivePort在每一個(gè)Isolate都有一對(duì)不跟,只有同一個(gè)Isolate中的ReceivePort才能接受當(dāng)前類的SendPort發(fā)送的消息并且處理窝革。
  • Isolate可以把它理解為Dart中的線程聊闯。但它又不同于線程米诉,更恰當(dāng)?shù)恼f應(yīng)該是微線程。它與線程最大的區(qū)別就是不能共享內(nèi)存魏身,因此也不存在鎖競(jìng)爭(zhēng)問題蚪腐,兩個(gè)Isolate完全是兩條獨(dú)立的執(zhí)行線回季,且每個(gè)Isolate都有自己的事件循環(huán)泡一,它們之間只能通過發(fā)送消息通信,所以它的資源開銷低于線程涵但。
    創(chuàng)建Isolate的兩種方式:Isolate.spawn() 和 compute()
    compute的使用還是有些限制矮瘟,它沒有辦法多次返回結(jié)果澈侠,也沒有辦法持續(xù)性的傳值計(jì)算埋酬,每次調(diào)用奇瘦,相當(dāng)于新建一個(gè)隔離耳标,如果調(diào)用過多的話反而會(huì)適得其反。
@override
  void initState() async {
    super.initState();
    // 主Isolate的ReceivePort
    ReceivePort receivePort = ReceivePort();
    SendPort? otherSendPort;
    // 主Isolate接收到子Isolate中由主Isolate的SendPort發(fā)送過來的消息
    receivePort.listen((message) {
      if(message is SendPort){
        otherSendPort = message;
        otherSendPort.send(receivePort.sendPort());
      }else{
        // 處理消息
        // ......
        
        // 子Isolate的SendPort在主Isolate中向子Isolate發(fā)送消息
       otherSendPort?.send('我是來自主Isolate的消息');
      }
    });

    // 創(chuàng)建子Isolate
    Isolate isolate = await Isolate.spawn((message) {
      // message 是主Isolate的SendPort

      // 在子Isolate中創(chuàng)建一個(gè)新的ReceivePort
      ReceivePort recPort = ReceivePort();
      // 主Isolate的SendPort
      SendPort? mainSendPort;
      
      // 運(yùn)用主Isolate的SendPort將子Isolate的SendPort發(fā)送給主Isolate
      message.send(recPort.sendPort);
      // 子Isolate監(jiān)聽接收到主Isolate那邊發(fā)送的消息 誰發(fā)送?子Isolate的SendPort
      recPort.listen((msg) {
        if(msg is SendPort){
          mainSendPort = msg;
        }else{
          // 主Isolate的SendPort向主Isolate發(fā)送消息
            mainSendPort?.send('我是來自子Isolate的消息');
        }
      });
    }, receivePort.sendPort);// 參數(shù)二 將主Isolate的SendPort傳遞給子Isolate
  }


// 方式二
// 注冊(cè)主 isolate 的 SendPort
  ReceivePort mainReceivePort = ReceivePort();
  mainReceivePort.listen((state){
    // 獲取上次執(zhí)行任務(wù)的時(shí)間戳
    final lastExecutedTime = SpUtil().getStandLastTime(); // 從本地存儲(chǔ)或數(shù)據(jù)庫獲取
    final now = DateTime.now().millisecondsSinceEpoch;
    final interval = now - lastExecutedTime;
    if (kDebugMode) {
      print("Native called background interval: $interval");
    }
    // 判斷是否達(dá)到 1 小時(shí)
    if (interval >= 3600000) {
      // 執(zhí)行你的任務(wù)
      /// 檢查步數(shù)是否在變化  沒有變化需要站立提醒
      GlobalEvent().emit(GlobalName.wearStandCheck);
      // 更新上次執(zhí)行時(shí)間
      SpUtil().setStandLastTime(now); // 將 now 保存到本地存儲(chǔ)或數(shù)據(jù)庫
    }

  });
  IsolateNameServer.registerPortWithName(mainReceivePort.sendPort, 'main_isolate');

  Workmanager().initialize(
      callbackDispatcher, // The top level function, aka callbackDispatcher
      isInDebugMode: false // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
  );
  /// Android系統(tǒng)限制 15分鐘執(zhí)行一次
  Workmanager().registerPeriodicTask(
    "periodic-task-identifier",
    "simplePeriodicTask",
    inputData: <String, dynamic>{
      'key': 'value',
    },
    // When no frequency is provided the default 15 minutes is set.
    // Minimum frequency is 15 min. Android will automatically change your frequency to 15 min if you have configured a lower frequency.
    frequency: const Duration(minutes: 15),
  );
await for如何使用?

await for是用來不斷獲取stream流中的數(shù)據(jù)谚赎,然后執(zhí)行循環(huán)體中的操作壶唤。它一般用在直到stream什么時(shí)候完成,并且必須等待傳遞完成后才能使用悯辙,不然會(huì)阻塞躲撰。

Stream<String> stream = new Stream<String>.fromIterable(['1', '2', '3','4']);
main() async{
    await for(String s in stream){
    print(s);
  }
}

使用場(chǎng)景:網(wǎng)絡(luò)請(qǐng)求流式返回

// http庫流式返回
Future<void> postStream({required String token,required String url, required Map<String, dynamic> body,required Function(String value) onMessage}) async {
    try {
      final request = http.Request('POST', Uri.parse(url));
      request.headers['Content-Type'] = 'application/json; charset=UTF-8';
      request.headers['Authorization'] = token;
      request.body = jsonEncode(body);

      final response = await request.send();
      if (response.statusCode == 200) {
        await for (var chunk in response.stream.transform(utf8.decoder)) {
          onMessage(chunk);
        }
      } else {
        throw Exception('服務(wù)器錯(cuò)誤');
      }
    }catch (e) {
      Logger.debugLog('Error: $e');
      throw Exception('服務(wù)器錯(cuò)誤');
    }
  }

// dio流式返回
Future<void> postStream({required String url, required Map<String, dynamic> body,required Function(String value) onMessage,CancelToken? cancelToken,}) async {
    try {
      Options requestOptions = Options(receiveTimeout: 60000,sendTimeout: 30000);
      requestOptions.headers = {
        ..._dio!.options.headers,
        'Authorization': Ability().getLoginToken(), 
        'Content-Type': 'application/json'
      };
      requestOptions.responseType = ResponseType.stream;
      Response<dynamic> response = await _dio!.post(
        url,
        data: jsonEncode(body),
        options: requestOptions,
        cancelToken: cancelToken,
      );
      if(response.statusCode == 200){
        await for (var chunk in response.data.stream) {
          final jsonData = utf8.decode(chunk);
          onMessage(jsonData);
        }
      }
    } catch (e) {
      // 處理請(qǐng)求失敗的情況
      print("postStream Request failed: $e");
      rethrow;
    }
  }
如何實(shí)現(xiàn)websocket穩(wěn)定連接?
  • 定期發(fā)送心跳包,一般1秒一次
  • 捕獲關(guān)閉連接事件并重連websocket
  • 實(shí)現(xiàn)斷線重連

Flutter部分

A批幌、B兩個(gè)組件在setState前修改背景顏色嗓节,是否會(huì)修改成功拦宣?

會(huì)

Flutter 渲染流程是什么鸵隧?(GPU)

將dart語言的UI代碼轉(zhuǎn)換成skia能識(shí)別的數(shù)據(jù),進(jìn)行渲染珊蟀。
Flutter向GPU提供視圖數(shù)據(jù)的過程育灸。
Flutter只關(guān)心向 GPU提供視圖數(shù)據(jù)磅崭,GPU的 VSync信號(hào)同步到 UI線程瓦哎,UI線程使用 Dart來構(gòu)建抽象的視圖結(jié)構(gòu),這份數(shù)據(jù)結(jié)構(gòu)在 GPU線程進(jìn)行圖層合成卒落,視圖數(shù)據(jù)提供給 Skia引擎渲染為 GPU數(shù)據(jù)儡毕,這些數(shù)據(jù)通過 OpenGL或者 Vulkan提供給 GPU扑媚。


20190227094157995.png

image.png
Widget费坊、Element附井、RenderObject三者關(guān)系是什么两残?

Widget包含業(yè)務(wù)代碼人弓,widget樹更龐大崔赌;Element是對(duì)widget的抽取,只包含build函數(shù)县钥,去除業(yè)務(wù)代碼魁蒜。

簡(jiǎn)述

widget是用于描述Element配置信息的兜看,flutter中一切都是widget狭瞎,尺寸熊锭、顏色、組件等都是widget
element是widget樹上特定位置的實(shí)例
renderobject是渲染樹上的一個(gè)對(duì)象

依賴關(guān)系:Element樹依賴Widget樹速缨,渲染樹依賴Element樹代乃,最終的UI樹是由獨(dú)立的Element節(jié)點(diǎn)構(gòu)成搁吓。

一個(gè)widget會(huì)創(chuàng)建一個(gè)element
一個(gè)element持有一個(gè)widget和render object原茅,element
會(huì)對(duì)比widget的變化,將那寫需要更新和重建的widget堕仔,同步到render object樹擂橘,以最小的開銷來渲染

一、它們是什么摩骨?
  • Widget:對(duì)一個(gè)Element配置的描述通贞,刷新的過程中隨時(shí)會(huì)重建。(不參與真正的渲染恼五,widget的屬性是不可以改變的,要想改變只能重新創(chuàng)建一個(gè)widget對(duì)象)
  • Element:表示一個(gè)Widget樹中特定位置的實(shí)例唤冈,用于對(duì)比widget,找出需要更新和重建的widget银伟,更新Element樹和RenderObject樹你虹。
  • RenderObject:渲染樹上的一個(gè)對(duì)象,用于界面的布局和繪制彤避,負(fù)責(zé)真正的渲染傅物,實(shí)例化一個(gè) RenderObject 是非常耗能。
二琉预、關(guān)系
  • 一個(gè)Widget會(huì)創(chuàng)建一個(gè)Element對(duì)象董饰,是通過createElement()創(chuàng)建的。
  • 一個(gè)Element持有一個(gè)RenderObject和一個(gè)Widget圆米。Element樹與Widget樹一一對(duì)應(yīng)卒暂,每個(gè)Element負(fù)責(zé)管理一個(gè)Widget的配置和生命周期。
  • Widget 具有不可變性娄帖,但 Element 卻是可變的也祠。Element 樹將 Widget 樹的變化做了抽象,可以只將真正需要修改的部分同步到真實(shí)的 RenderObject 樹中近速,最大程度降低對(duì)真實(shí)渲染視圖的修改诈嘿,提高渲染效率堪旧,而不是銷毀整個(gè)渲染視圖樹重建。
flutter生命周期奖亚,setstate會(huì)執(zhí)行哪些生命周期淳梦?
image.png
image.png
  • setState()執(zhí)行后,會(huì)執(zhí)行build()
  • 父Widget使用了InheritedWidget管理狀態(tài)昔字,子Widget使用了狀態(tài)數(shù)據(jù)爆袍,set State時(shí),子組件的didChangeDependencies會(huì)調(diào)用李滴。

https://juejin.cn/post/7348680291935862818?searchId=20240410133635350846A909709A89034C

  • app的狀態(tài):AppLifecycleState
    inactive:活躍可見
    paused:關(guān)閉或者切換到后臺(tái)時(shí)螃宙,不可見的狀態(tài)
    hidden:后臺(tái)運(yùn)行狀態(tài)
    resumed:切回到前臺(tái)可見狀態(tài)
    detached:關(guān)閉狀態(tài)
  • Flutter SDK 3.13 之前的方式: with WidgetsBindingObserver
    initState()中注冊(cè) WidgetsBinding.instance.addObserver(this);
    dispose()移除 WidgetsBinding.instance.removeObserver(this);
    didChangeAppLifecycleState()回調(diào)中,檢測(cè)app的狀態(tài)

  • Flutter SDK 3.13 之后的方式:AppLifecycleListener

late final AppLifecycleListener _listener;
@override
  void initState() {
    super.initState();
    // Initialize the AppLifecycleListener class and pass callbacks
    _listener = AppLifecycleListener(
      onStateChange: _onStateChanged,
    );
  }
  @override
  void dispose() {
    // Do not forget to dispose the listener
    _listener.dispose();
    super.dispose();
  }
  // Listen to the app lifecycle state changes
  void _onStateChanged(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.detached:
        _onDetached();
      case AppLifecycleState.resumed:
        _onResumed();
      case AppLifecycleState.inactive:
        _onInactive();
      case AppLifecycleState.hidden:
        _onHidden();
      case AppLifecycleState.paused:
        _onPaused();
    }
  }
StatefulWidget 的生命周期方法

createState:可以調(diào)用多次

State 對(duì)象的生命周期方法
class _MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();
    // 在 State 對(duì)象被插入樹中時(shí)調(diào)用所坯,這個(gè)方法只會(huì)被調(diào)用一次谆扎。
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 在 initState 之后調(diào)用,表示 State 對(duì)象的依賴關(guān)系發(fā)生變化芹助。
    // state對(duì)象依賴發(fā)生改變會(huì)調(diào)用
  }
  @override
  Widget build(BuildContext context) {
    // 在此構(gòu)建 Widget 樹
    return Container();
  }
  @override
  void didUpdateWidget(MyWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 當(dāng)父 Widget 重建時(shí)堂湖,canUpdate 返回true時(shí)調(diào)用。
    // 如果父 Widget 重建時(shí)需要重新配置子 Widget状土,則會(huì)調(diào)用此方法无蜂。
  }
  @override
  void deactivate() {
    // 在此處理 State 對(duì)象從樹中被移除的操作
    super.deactivate();
  }

  @override
  void dispose() {
    // 當(dāng) State 對(duì)象被永久從樹中移除時(shí)調(diào)用
    super.dispose();
  }
}

// 打開頁面執(zhí)行:initState、didChangeDependencies
// 關(guān)閉頁面執(zhí)行:deactivate蒙谓、dispose

StatelessWidget 的生命周期方法
class MyWidget extends StatefulWidget {
  @override
  Widget build(BuildContext context) {
    return Widget;
  }
}

StatelessWidget組件如何監(jiān)聽數(shù)據(jù)變化斥季?

  1. context.watch<Counter>
  2. ChangeNotifierProvider + Consumer

Element的生命周期

initial 初始化
active 激活狀態(tài)
inactive 未激活狀態(tài)
defunct 失效狀態(tài)

如何監(jiān)聽頁面paused和resume狀態(tài)?

利用RouteObserverMaterialApp中注冊(cè)navigatorObservers累驮,然后在頁面中注冊(cè)監(jiān)聽routeObserver.subscribe(this, ModalRoute.of(context)!)酣倾,同時(shí)混入RouteAware,重寫didPopNext 和 didPushNext谤专,可實(shí)現(xiàn)對(duì)頁面的監(jiān)聽躁锡。

setState()執(zhí)行做了什么事?

setState()過程主要工作是記錄所有的臟元素置侍,會(huì)引起build函數(shù)執(zhí)行映之,更新widget樹、更新Element樹和RenderObject樹蜡坊,最后重新渲染杠输。

flutter中的key?
  • 作用:比較兩個(gè)Widget是不是同一個(gè)Widget
  • 分類:LocaleKey秕衙、GlobalKey
  • LocaleKey:ValueKey抬伺、ObjectKey、UniqueKey灾梦。
statelesswidget和statefullwidget有什么區(qū)別峡钓?

StatelessWidget 沒有要管理的內(nèi)部狀態(tài).
無狀態(tài)widget的build方法通常只會(huì)在以下三種情況調(diào)用:

  • 將widget插入樹中時(shí)
  • 當(dāng)widget的父級(jí)更改其配置時(shí)
  • 當(dāng)它依賴的InheritedWidget發(fā)生變化時(shí)

StatefullWidget是可變狀態(tài)的widget妓笙。 使用setState方法管理StatefulWidget的狀態(tài)的改變。調(diào)用setState告訴Flutter框架能岩,某個(gè)狀態(tài)發(fā)生了變化寞宫,F(xiàn)lutter會(huì)重新運(yùn)行build方法,以便應(yīng)用程序可以應(yīng)用最新狀態(tài)拉鹃。

在有狀態(tài)類中編寫一個(gè)按鈕調(diào)用初始化生命周期(initState)方法辈赋,會(huì)發(fā)生什么?

報(bào)錯(cuò)膏燕,但不影響布局钥屈;會(huì)報(bào)生命周期創(chuàng)建錯(cuò)誤;

如何獲取控件的大小和位置坝辫?
  1. 使用Key拿到上下文取得findRenderObject拿內(nèi)容的尺寸數(shù)據(jù)篷就;
  2. 使用context取得findRenderObject拿內(nèi)容的尺寸數(shù)據(jù);
Flutter 是如何與原生Android近忙、iOS進(jìn)行通信的竭业?

PlatformChannel
BasicMessageChannel :用于傳遞字符串和半結(jié)構(gòu)化的信息。
MethodChannel :用于傳遞方法調(diào)用(method invocation)及舍。
EventChannel : 用于數(shù)據(jù)流(event streams)的通信未辆。

// 1. 創(chuàng)建java類。  實(shí)現(xiàn)FlutterPlugin和MethodCallHandler
 public class MsaOaidPlugin implements FlutterPlugin, MethodCallHandler{
   @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    Log.e("---------","==========onAttachedToEngine");
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "msa_oaid");
    channel.setMethodCallHandler(this);
    this.context = flutterPluginBinding.getApplicationContext();
    System.loadLibrary("msaoaidsec");

  }
  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    Log.e("---------","==========onMethodCall");
    if(call.method.equals("isSupport")){
      new DemoHelper().getDeviceIds(context, new IIdentifierListener() {
        @Override
        public void onSupport(IdSupplier idSupplier) {
          result.success(idSupplier.isSupported());
        }
      });
    }else if(call.method.equals("getOaid")){
      new DemoHelper().getDeviceIds(context, new IIdentifierListener() {
        @Override
        public void onSupport(IdSupplier idSupplier) {
          result.success(idSupplier.getOAID());
        }
      });

    }else{
      result.notImplemented();
    }

  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
    Log.e("---------","==========onDetachedFromEngine");
  }
 }


// 2. flutter lib包下創(chuàng)建dart類

class MsaOaid {
  static const MethodChannel _channel = MethodChannel('msa_oaid');

  static Future<bool>  isSupport() async {
    final bool support = await _channel.invokeMethod('isSupport');
    return support;
  }

  static Future<String?> getOaid() async {
    final String? oaid = await _channel.invokeMethod('getOaid');
    return oaid;
  }
}
Flutter中Widget的分類有哪些锯玛?Widget狀態(tài)有哪些咐柜?

Widget的分類有三類。

  1. 組合類Widget,通過繼承StatelessWidget和StatefulWidget的類。
  2. 代理類Widget,如功能組件InheritedWidget。Theme蔬浙、MediaQuery正是基于InheritedWidget實(shí)現(xiàn)的。
  3. 繪制類Widget浩螺,通過RenderObjectWidget實(shí)現(xiàn)的Widget疫剃,如Align、Padding涉瘾、ConstrainedBox等知态。
    Widget狀態(tài)有: StatelessWidget 和 StatefulWidget

Android部分

實(shí)現(xiàn)app保活有哪些方式立叛?
計(jì)步器在app進(jìn)程被殺這段時(shí)間如何計(jì)步负敏?

補(bǔ)充

https://zhuanlan.zhihu.com/p/102193331

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市秘蛇,隨后出現(xiàn)的幾起案子其做,更是在濱河造成了極大的恐慌顶考,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妖泄,死亡現(xiàn)場(chǎng)離奇詭異驹沿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹈胡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門渊季,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人罚渐,你說我怎么就攤上這事却汉。” “怎么了荷并?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵合砂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我璧坟,道長(zhǎng)既穆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任雀鹃,我火速辦了婚禮幻工,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘黎茎。我一直安慰自己囊颅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布傅瞻。 她就那樣靜靜地躺著踢代,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嗅骄。 梳的紋絲不亂的頭發(fā)上胳挎,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音溺森,去河邊找鬼慕爬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛屏积,可吹牛的內(nèi)容都是我干的医窿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼炊林,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼姥卢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤独榴,失蹤者是張志新(化名)和其女友劉穎僧叉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棺榔,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡彪标,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掷豺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捞烟。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖当船,靈堂內(nèi)的尸體忽然破棺而出题画,到底是詐尸還是另有隱情,我是刑警寧澤德频,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布苍息,位于F島的核電站,受9級(jí)特大地震影響壹置,放射性物質(zhì)發(fā)生泄漏竞思。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一钞护、第九天 我趴在偏房一處隱蔽的房頂上張望盖喷。 院中可真熱鬧,春花似錦难咕、人聲如沸课梳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暮刃。三九已至,卻和暖如春爆土,著一層夾襖步出監(jiān)牢的瞬間椭懊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工步势, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留氧猬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓立润,卻偏偏與公主長(zhǎng)得像狂窑,于是被迫代替她去往敵國(guó)和親媳板。 傳聞我的和親對(duì)象是個(gè)殘疾皇子桑腮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一、flutter與原生通信蛉幸,三種通道的區(qū)別 1.1 MethodChannel Flutter與Native端相...
    耿宏達(dá)閱讀 1,353評(píng)論 0 2
  • 1破讨、Dart中var 與 dynamic的區(qū)別 2丛晦、const和final的區(qū)別 3、Dart中 ?? 與 ??=...
    永不放棄_8eef閱讀 1,445評(píng)論 0 3
  • 又到了面試題分享的時(shí)間提陶,今天分享的是Flutter方面的的面試題烫沙,總共包含以下板塊: Dart部分Flutter部...
    小城哇哇閱讀 235評(píng)論 0 1
  • Flutter是Google推出的一套開源跨平臺(tái)UI框架,可以快速地在Android隙笆、iOS和Web平臺(tái)上構(gòu)建高質(zhì)...
    GoldMask閱讀 15,388評(píng)論 2 48
  • 1. Dart 當(dāng)中的 「..」表示什么意思锌蓄? Dart 當(dāng)中的 「..」意思是 「級(jí)聯(lián)操作符」,為了方便配置而使...
    馬修斯閱讀 13,726評(píng)論 0 22