最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Flutter實現(xiàn)webview與原生組件組合滑動的示例代碼

 更新時間:2019年03月27日 10:42:22   作者:YouCii  
這篇文章主要介紹了Flutter實現(xiàn)webview與原生組件組合滑動的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

最近在用Flutter寫一個新聞客戶端, 新聞詳情頁中的內(nèi)容 需要用Flutter的本地Widget和WebView共同展示 . 比如標(biāo)題/上方的視頻播放器是用本地Widget展示, 新聞內(nèi)容的富文本文字使用webview展示html, 這樣就要求標(biāo)題/視頻播放器與webview可以 組合滑動 .

ps: 如果把新聞詳情頁都用html畫出, 就不用考慮組合滑動的問題.

找到支持與本地組件共存的webview控件

找一個可以與本地組件共存的webview控件是首要任務(wù), 以下是我測試過的幾個庫:

  1. flutter_WebView_plugin : 不可以inline;
  2. webView_flutter : 可能支持, 但是還沒有發(fā)布;
  3. flutter_inappbrowser : 可以實現(xiàn)組合布局, 所以選用了此庫, 鏈接 https://github.com/pichillilorenzo/flutter_inappbrowser

另外, 如果僅是展示html靜態(tài)頁面, 可以嘗試以下幾個庫, 不用看我這個麻煩的解決辦法了:

html
flutter_html
flutter_html_view

初步實現(xiàn)組合布局

選定 flutter_inappbrowser 后開始實現(xiàn), 初步代碼如下:

@override
 Widget build(BuildContext context) {
  return Scaffold(
   appBar: AppBar(),
   body: Column(
    children: <Widget>[
     Text('Title'),
     Expanded( // 注意必須加這個, 否則webview沒有高度
      child: InAppWebView(initialUrl: 'https://juejin.im/timeline'),
     ),
    ],
   ),
  );
 }

這樣會構(gòu)建一個text和webview組合的界面, 不過這里webview自帶滾動條, 滾動時是不帶著title一塊的. 嘗試以下兩種辦法

包裹 SingleChildScrollView : 界面會消失不見, 因為Scrollview根據(jù)子布局處理高度, 而Expanded又要根據(jù)父布局處理高度, 所以互相依賴導(dǎo)致整個頁面無法繪制.

body: SingleChildScrollView(
    child: Column(
     children: <Widget>[
      Text('Title'),
      Expanded(
       child: InAppWebView(initialUrl: 'https://juejin.im/timeline'),
      ),
     ],
    ),
   ),

包裹 SingleChildScrollView , 去掉 Expanded : AppBar可以顯示了, 但是 InAppWebView 沒有高度了.

body: SingleChildScrollView(
    child: Column(
     children: <Widget>[
      Text('Title'),
      InAppWebView(initialUrl: 'https://juejin.im/timeline'),
     ],
    ),
   ),

這兩種方式都不行, 歸根到底是不知道 InAppWebView 的高度, 所以才需要使用與 SingleChildScrollView 相沖突的 Expanded , 所以這個問題變?yōu)榱?如何獲取WebView的高度 .

獲取WebView的高度

在android中不會有這個破問題, 給 webview 設(shè)置 wrap_content 就可以了, 但是在Flutter中我沒有找到類似布局方式. (有大哥知道的話麻煩告訴我一下下啊)

其他嘗試的方法就不說了, 最后我采用的辦法是: 通過JS注入拿到html內(nèi)容的高度回調(diào) . 實現(xiàn)方法如下:

class TestState extends State<Test> {
 InAppWebViewController _controller;
 double _htmlHeight = 200; // 目的是在回調(diào)完成直接先展示出200高度的內(nèi)容, 提高用戶體驗

 static const String HANDLER_NAME = 'InAppWebView';

 @override
 void dispose() {
  super.dispose();
  _controller?.removeJavaScriptHandler(HANDLER_NAME, 0);
  _controller = null;
 }

 @override
 Widget build(BuildContext context) {
  return Scaffold(
   appBar: AppBar(),
   body: SingleChildScrollView(
    child: Column(
     children: <Widget>[
      Text('Title'),
      Container( // 使用可提供高度的Container包裹WebView, 設(shè)置為回調(diào)的高度
       height: _htmlHeight,
       child: InAppWebView(
        initialUrl: 'https://juejin.im/timeline',
        onWebViewCreated: (InAppWebViewController controller) {
         _controller = controller;
         _setJSHandler(_controller); // 設(shè)置js方法回掉, 拿到高度
        },
        onLoadStop: (InAppWebViewController controller, String url) {
         // 頁面加載完成后注入js方法, 獲取頁面總高度  
         controller.injectScriptCode("""
         window.flutter_inappbrowser.callHandler('InAppWebView', document.body.scrollHeight));
        """);
        },
       ),
      )
     ],
    ),
   ),
  );
 }

 void _setJSHandler(InAppWebViewController controller) {
  JavaScriptHandlerCallback callback = (List<dynamic> arguments) async {
   // 解析argument, 獲取到高度, 直接設(shè)置即可(iphone手機(jī)需要+20高度)
   double height = HtmlUtils.getHeight(arguments);
   if (height > 0) {
    setState(() {
     _htmlHeight = height;
    });
   }
  };
  controller.addJavaScriptHandler(HANDLER_NAME, callback);
 }
}

以上方法可以精確獲取到webview高度, 實現(xiàn)webview與本地Widget組合滑動的要求.

Android端一個問題

以上方法實現(xiàn)后我是一陣竊喜, 趕忙測試了一下, 結(jié)果發(fā)現(xiàn)一個嚴(yán)重問題: Android端給webview設(shè)置超出5500左右的高度時, App會閃退 . 閃退時AndroidStudio不會展示錯誤日志, 通過 flutter run --verbose 命令運(yùn)行可以獲取到錯誤信息, 大體看了下是Flutter渲染的問題, 先反饋給官方以及 flutter_inappbrowser 作者了.

然后自己簡單測試發(fā)現(xiàn), 給Column的child添加了多個webview沒什么問題, 哪怕這幾個webview的內(nèi)容相加絕對超出了5500高度. 所以有了思路: 切分html, 分為多個webview共同展示, 然后分別注入JS獲取高度 .

注意!注意! 我們的使用場景是: 要展示的內(nèi)容 = assets存儲的html外殼 + 接口獲取到的新聞內(nèi)容段落, 而不是一個url . 以上解決思路僅適用于加載html的場景, 而不是url.

這個思路的核心在于如何切分html內(nèi)容, 需要保證切分后的html是標(biāo)簽閉合的, 即不是切在了某標(biāo)簽內(nèi)部. 使用此切分方案的前提是: body內(nèi)部的html標(biāo)簽不會有超大范圍的div包裹, 否則單個標(biāo)簽內(nèi)容就超過高度了. 可用的html示例:

<html>
 <head></head>
  <body>
    <!-- 并列小組合, 沒有超大范圍的div等標(biāo)簽的包裹 -->
    <p style.. > asdasdasd </p>
    <div style.. > 
      <img ... />
      <p> ... </p>
    </div> 
    <p> asdasdas </p>
  </body>
</html>

下面是我實現(xiàn)的切分html的算法:

// 剪切過長的html, 考慮到較差機(jī)型以及其他誤差, 定為4000
 // @params htmlString 待切分的html
 // @params totalHeight 前面webview回調(diào)出的總高度
 // @return String 剪切后的html
 static List<String> cutHtml(String htmlString, double totalHeight) {
  htmlString = _getBody(htmlString);

  List<String> htmlList = List();
  if (Platform.isAndroid && totalHeight > 4000) {
   // 切為幾段('~/'整除, /.toInt)
   int childNum = totalHeight ~/ 4000 + (totalHeight % 4000 == 0 ? 0 : 1);
   // 每段html的長度
   int childLength = htmlString.length ~/ childNum;
   // 切一刀后的兩段html
   String resultHtml = '', remainHtml = htmlString;

   int labelStack = 0;
   while (childNum > 0 && remainHtml.length > 0) {
    if (childLength < remainHtml.length) {
     resultHtml = remainHtml.substring(0, childLength);
     remainHtml = remainHtml.substring(childLength);
    } else {
     resultHtml = remainHtml;
     remainHtml = '';
    }

    if (_checkComplete(resultHtml, labelStack)) {
     htmlList.add(resultHtml);
     childNum--;
    } else {
     // 如果不是閉合的, 把remain里的n個標(biāo)簽尾之前的內(nèi)容剪切到result中
     while (labelStack != 0) {
      int tailPosition = remainHtml.indexOf(_labelsTail);
      if (tailPosition != -1) {
       resultHtml = resultHtml + remainHtml.substring(0, tailPosition + 2);
       remainHtml = remainHtml.substring(tailPosition + 2);
       labelStack--;
      }
     }
     htmlList.add(resultHtml);
     childNum--;
    }
   }
  } else {
   htmlList.add(htmlString);
  }

  return htmlList;
 }

 // true if resultHtml是標(biāo)簽閉合的
 static bool _checkComplete(String resultHtml, int labelStack) {
  labelStack = 0;
  for (int i = 0; i < resultHtml.length; i++) {
   if (resultHtml.startsWith('<', i)) {
    String label = _startWithLabel(resultHtml.substring(i));
    if (label != null) {
     labelStack++;
     i += label.length - 1;
    }
   }
   if (resultHtml.startsWith(_labelsTail, i)) {
    labelStack--;
    i += _labelsTail.length - 1;
   }
  }
  return labelStack == 0;
 }

 // 以_labelsHead內(nèi)的字符串開頭
 static String _startWithLabel(String resultHtml) {
  for (String label in _labelsHead) {
   if (resultHtml.startsWith(label)) {
    return label;
   }
  }
  return null;
 }

 // 去除body及以外的標(biāo)簽, 露出并列的子標(biāo)簽
 // <html>
 //  <head></head>
 //   <body>
 //   ...
 //   </body>
 // </html>
 static String _getBody(String htmlString) {
  if (htmlString.contains('<body>')) {
   htmlString = htmlString.substring(htmlString.indexOf('<body>') + 6);
   htmlString = htmlString.substring(0, htmlString.indexOf('</body>'));
  }
  return htmlString;
 }

 // 待檢測的標(biāo)簽
 static final _labelsHead = {'<div', '<img', '<p', '<strong', '<span'};
 static final _labelsTail = '</';

通過以上算法, 拿到了切分好的htmlList, 然后在PageState中使用多個webview分別加載, 分別注入js即可解決此問題.

大功告成!

附:

flutter_inappbrowser 如何加載html字符串:

InAppWebView( initialData: InAppWebViewInitialData(' htmlContent '))

解析asset文件為字符串:

static Future<String> decodeStringFromAssets(String path) async {
  ByteData byteData = await PlatformAssetBundle().load(path);
  String htmlString = String.fromCharCodes(byteData.buffer.asUint8List());
  return htmlString;
}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android自定義View實現(xiàn)LayoutParams的方法詳解

    Android自定義View實現(xiàn)LayoutParams的方法詳解

    這篇文章主要為大家詳細(xì)介紹了Android自定義View實現(xiàn)LayoutParams,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-02-02
  • Android實現(xiàn)圖片點(diǎn)擊放大

    Android實現(xiàn)圖片點(diǎn)擊放大

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)圖片點(diǎn)擊放大,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • Android開發(fā)兩個activity之間傳值示例詳解

    Android開發(fā)兩個activity之間傳值示例詳解

    這篇文章主要為大家介紹了Android開發(fā)兩個activity之間傳值示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Android  onSaveInstanceState和onRestoreInstanceState觸發(fā)的時機(jī)

    Android onSaveInstanceState和onRestoreInstanceState觸發(fā)的時機(jī)

    這篇文章主要介紹了Android onSaveInstanceState和onRestoreInstanceState觸發(fā)的時機(jī)的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Android實現(xiàn)調(diào)用震動的方法

    Android實現(xiàn)調(diào)用震動的方法

    這篇文章主要介紹了Android實現(xiàn)調(diào)用震動的方法,實例分析了Android中Vibrator類的調(diào)用與使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11
  • Android 控制ScrollView滾動的實例詳解

    Android 控制ScrollView滾動的實例詳解

    這篇文章主要介紹了Android 控制ScrollView滾動的實例詳解的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-10-10
  • 教你如何搭建android源代碼repo倉庫

    教你如何搭建android源代碼repo倉庫

    這篇文章主要介紹了如何搭建android源代碼repo倉庫,如果你的開發(fā)是基于AOSP源碼來建倉,那么搭建repo服務(wù)器和部署自己的repo倉庫就是非常必要的工作了,本文給大家詳細(xì)介紹搭建過程,感興趣的朋友一起看看吧
    2022-07-07
  • 替換so文件來動態(tài)替換Flutter代碼實現(xiàn)詳解

    替換so文件來動態(tài)替換Flutter代碼實現(xiàn)詳解

    這篇文章主要為大家介紹了替換so文件來動態(tài)替換Flutter代碼實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • 使用Flutter開發(fā)一個圖片UI組件的代碼示例

    使用Flutter開發(fā)一個圖片UI組件的代碼示例

    在移動應(yīng)用開發(fā)中,圖片展示是一個常見的需求,為了滿足不同場景的圖片展示需求,我們可以開發(fā)一個靈活配置的圖片UI組件,本文將介紹如何使用Flutter開發(fā)一個圖片UI組件,并提供了豐富的配置選項,需要的朋友可以參考下
    2023-09-09
  • Android利用Fragment實現(xiàn)Tab選項卡效果

    Android利用Fragment實現(xiàn)Tab選項卡效果

    這篇文章主要為大家詳細(xì)介紹了Android利用Fragment實現(xiàn)Tab選項卡效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-08-08

最新評論

洪洞县| 赞皇县| 塔城市| 南雄市| 固镇县| 东丽区| 杂多县| 莆田市| 进贤县| 泰兴市| 佳木斯市| 驻马店市| 高要市| 韩城市| 哈巴河县| 丘北县| 长寿区| 吴川市| 霸州市| 綦江县| 普洱| 从化市| 宜黄县| 怀来县| 德安县| 昂仁县| 渝北区| 济宁市| 特克斯县| 重庆市| 河北区| 乌拉特前旗| 康平县| 揭东县| 囊谦县| 贵南县| 肥东县| 霍山县| 余姚市| 无为县| 丰原市|