Flutter攜程APP頁面實(shí)現(xiàn)

SeacherBar 搜索頁面

目錄

  • SeacherBar
  • 語音界面
  • 百度SDK接入
image.png

對(duì)InputDecoration進(jìn)行了封裝,擁有多種成員函數(shù),是否禁止搜索、按鈕隱藏、背景顏色、函數(shù)回調(diào)等多種功能。
final void Function() leftButtonClick; 將函數(shù)從其他地方傳入,加大擴(kuò)展性。
_wrapTap() 封裝函數(shù)對(duì)可點(diǎn)擊的按鈕進(jìn)行封裝
ValueChanged<String> 文本內(nèi)容變換監(jiān)聽,用于改變按鈕的樣式
優(yōu)化:原因:由于每次文本變化需要從網(wǎng)絡(luò)拉取數(shù)據(jù),可能造成用戶輸入的內(nèi)容不是真正需要顯示的內(nèi)容。
解決:在網(wǎng)絡(luò)請(qǐng)求方法保存keyword。檢查請(qǐng)求來的keyword是否一樣。檢查Widget重繪的次數(shù),并且解決BUG。

_wrapTap(Widget child, void Function() callback) {
  return GestureDetector(
     onTap: () {
       if (callback != null) callback();
     },
     child: child,
   );
 }

  SearchDao.fetch(url, text).then((SearchModel model) {
      // 只有當(dāng)當(dāng)前輸入的內(nèi)容與服務(wù)端返回的內(nèi)容一致才渲染
      if (model.keyword == keyword) {// 檢驗(yàn)keyword
        setState(() {
          searchModel = model;
        });
      }
    }).catchError((e) {
      print(e);
    });

語音界面

image.png

CurvedAnimation將動(dòng)畫過程定義為一個(gè)非線性曲線。
addStatusListener:監(jiān)聽動(dòng)畫執(zhí)行的狀態(tài),completed狀態(tài)反向執(zhí)行,dismissed狀態(tài)從新開始動(dòng)畫。

@override
  void initState() {
    controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: 1000));// 初始化controller
    animation = CurvedAnimation(parent: controller, curve: Curves.easeIn)
      ..addStatusListener((status) {
        // 動(dòng)畫執(zhí)行完畢,希望循環(huán)執(zhí)行
        if (status == AnimationStatus.completed) {
          controller.reverse(); // 反向執(zhí)行
        } else if (status == AnimationStatus.dismissed) {
          controller.forward(); // 開始動(dòng)畫
        }
      });
    super.initState();
  }

class AnimatedMic extends AnimatedWidget {
  static final _opacityTween = Tween<double>(begin: 1, end: 0.5); // 透明度 1 到 0.5
  static final _sizeTween =
      Tween<double>(begin: MIC_SIZE, end: MIC_SIZE - 20.0); // 大小變化

  AnimatedMic({Key key, Animation<double> animation})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;

    return Opacity(
      opacity: _opacityTween.evaluate(animation),
      child: Container(
        height: _sizeTween.evaluate(animation),
        width: _sizeTween.evaluate(animation),
        decoration: BoxDecoration(
          color: Colors.blue,
          borderRadius: BorderRadius.circular(MIC_SIZE / 2),
        ),
        child: Icon(
          Icons.mic,
          color: Colors.white,
          size: 30,
        ),
      ),
    );
  }
}

Animation:在Flutter中,Animation對(duì)象本身和UI渲染沒有任何關(guān)系。Animation是一個(gè)抽象類,它擁有其當(dāng)前值和狀態(tài)(完成或停止)。其中一個(gè)比較常用的Animation類是Animation<double style="box-sizing: border-box;">。</double>
Flutter中的Animation對(duì)象是一個(gè)在一段時(shí)間內(nèi)依次生成一個(gè)區(qū)間之間值的類Animation對(duì)象的輸出可以是線性的、曲線的、一個(gè)步進(jìn)函數(shù)或者任何其他可以設(shè)計(jì)的映射。 根據(jù)Animation對(duì)象的控制方式,動(dòng)畫可以反向運(yùn)行,甚至可以在中間切換方向。

百度語音識(shí)別SDK

MethodChannel的定義

final MethodChannel _channel = const MethodChannel('asr_plugin');

重點(diǎn)來了,我們要實(shí)現(xiàn)FlutteriOSAndroid的交互就是通過這個(gè)MethodChannel。MethodChannel就是我們的信使,負(fù)責(zé)dart和原生代碼通信。com.jarvanmo/fluwx是MethodChannel的名字,flutter通過一個(gè)具體的名字能才夠在對(duì)應(yīng)平臺(tái)上找到對(duì)應(yīng)的MethodChannel,從而實(shí)現(xiàn)flutter與平臺(tái)的交互。同樣地,我們?cè)趯?duì)應(yīng)的平臺(tái)上也要注冊(cè)名為com.jarvanmo/fluwx的MethodChannel。

 public static void registerWith(PluginRegistry.Registrar registrar) {
        // 實(shí)例化MethodChannel 與 Dart關(guān)聯(lián)
        MethodChannel channel = new MethodChannel(registrar.messenger(), "asr_plugin");
        AsrPlugin instance = new AsrPlugin(registrar);
        // 處理Dart端消息
        channel.setMethodCallHandler(instance);
    }

Flutter與原生通信
我們將傳進(jìn)來的參數(shù)重新組裝成了Map并傳遞給了invokeMethod。其中invokeMethod函數(shù)第一個(gè)參數(shù)為函數(shù)名稱,即registerApp,我們將在原生平臺(tái)用到這個(gè)名字。第二個(gè)參數(shù)為要傳遞給原生的數(shù)據(jù)。第二個(gè)參數(shù)是dynamic的,那么我們是否可以傳遞任何數(shù)據(jù)類型呢?至少語法上是沒有錯(cuò)誤的,但實(shí)際上這是不允許的,只有對(duì)應(yīng)平臺(tái)的codec支持的類型才能進(jìn)行傳遞,也就是上文提到的數(shù)據(jù)類型對(duì)應(yīng)表,這條規(guī)則同樣適用于返回值,也就是原生給Flutter傳值。請(qǐng)記住這條規(guī)定,不再做贅述。

// 開始錄音
  static Future<String> start({Map params}) async {
    return await _channel.invokeMethod('start', params ?? {});
  }

如何在原生接收Flutter傳遞過來的數(shù)據(jù)?
上面我們將數(shù)據(jù)通過Flutter傳遞給了原生,我們要原生代碼里進(jìn)行接收與處理

@Override
    public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
        initPermission();
        switch (methodCall.method) {
            case "start":
                resultStateful = ResultStateful.of(result);
                start(methodCall, resultStateful);
                break;
            case "stop":
                stop(methodCall, result);
                break;
            case "cancel":
                cancel(methodCall, result);
                break;
            default:
                result.notImplemented();
        }
    }

call.method是方法名稱,我們要通過方法名稱比對(duì)完成調(diào)用匹配。當(dāng)call.method == "registerApp"成立時(shí),說明我們要調(diào)用registerApp,從而進(jìn)行更多的操作。此時(shí)可能會(huì)有同學(xué)問,如發(fā)現(xiàn)call.method不存在怎么辦?很簡單,我們可以通過resultFlutter報(bào)告一下該方法沒實(shí)現(xiàn):
當(dāng)調(diào)用這個(gè)方法之后,我們會(huì)在Flutter層收到一個(gè)沒實(shí)現(xiàn)該方法的異常。
如何在原生接收Flutter傳遞過來的數(shù)據(jù)?
我們可以通過result向Flutter報(bào)告一下該方法沒實(shí)現(xiàn)

  @Override
        public void onAsrFinalResult(String[] results, RecogResult recogResult) {
            if (resultStateful != null) {
                resultStateful.success(results[0]);
            }
        }

參考

帶你輕松掌握Flutter 動(dòng)畫開發(fā)核心技能

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容