主頁 > 移動端開發 > Flutter 知識點總結-進階篇

Flutter 知識點總結-進階篇

2021-07-31 08:37:37 移動端開發

文章目錄

    • 1、http get請求資料、post提交資料、以及渲染動態資料
    • 2、Dio庫實作網路請求以及動態渲染資料
    • 3、下拉重繪 上拉分頁加載更多
    • 4、實作簡單的新聞系統渲染新聞詳情資料以及用flutter_html決議html
    • 5、使用WebView組件flutter_inappbrowser加載遠程web頁面渲染新聞詳情資料
    • 6、獲取設備資訊 以及 使用高德Api獲取地理位置
    • 7、呼叫原生硬體Api實作照相機拍照和相冊選擇 以及拍照上傳到服務器
    • 8、實作視頻播放
    • 9、檢測網路連接,監聽網路變化
    • 10、 本地存盤,封裝本地存盤類,實作最簡單的狀態管理
    • 11、呼叫原生硬體Api實作掃碼 掃描條形碼 掃描二維碼
    • 12、檢測應用版本號、服務器下載檔案以及實作App自動升級、安裝
    • 13、打開外部瀏覽器、打開外部應用、撥打電話、發送短信
    • 14、支付寶支付【上】
    • 15、支付寶支付【下】
    • 16、ListView嵌套GridView、不同終端螢屏適配方案
    • 17、JSON的序列化和反序列化、創建模型類轉換Json資料
    • 18、底部 Tab 切換保持頁面狀態的幾種方法
    • 19、inappbrowser、StatefulBuilder 更新 Flutter showDialog、showModalBottomSheet中的狀態
    • 20、官方推薦的狀態管理庫 provider 的使用
    • 21、事件廣播 、事件監聽
    • 22、點擊穿透問題、頁面禁止左右滑動

喜歡記得點個贊喲,我是王睿,很高興認識大家!

1、http get請求資料、post提交資料、以及渲染動態資料

效果圖:

在這里插入圖片描述

在這里插入圖片描述

匯入: http: ^0.12.0+2庫

Http_Back.dart

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class HttpDemo extends StatefulWidget {
  HttpDemo({Key key}) : super(key: key);

  _HttpDemoState createState() => _HttpDemoState();
}

class _HttpDemoState extends State<HttpDemo> {

  List _list=[];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getData();
  }
  _getData() async{
    var apiUrl="http://a.itying.com/api/productlist";
    var result=await http.get(apiUrl);
    if(result.statusCode==200){
      print(result.body);
      setState(() {
       this._list=json.decode(result.body)["result"];

      /*
      {
        "result": [{
            "_id": "5ac0896ca880f20358495508",
            "title": "精選熱菜",
            "pid": "0",		
          }, {
            "_id": "5ac089e4a880f20358495509",
            "title": "特色菜",
            "pid": "0",
            
          }
        ]
      }

      */

      });
    }else{
      print("失敗${result.statusCode}");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("請求資料Demo"),
      ),
      body: this._list.length>0?ListView(
        children: this._list.map((value){
            return ListTile(
              title: Text(value["title"]),
            );
        }).toList(),
      ):Text("加載中...")
    );
  }
}

請求資料

  //請求資料
  _getData() async{

    var apiUrl="http://192.168.0.5:3000/news";

    var result=await http.get(apiUrl);
    if(result.statusCode==200){
      // print(json.decode(result.body));
      setState(() {
         this._news=json.decode(result.body)["msg"]; 
      });
    }else{
      print(result.statusCode);
    }
  }

提交資料

  _postData() async{
    var apiUrl="http://192.168.0.5:3000/dologin";

    var result=await http.post(apiUrl, body: {'username': '張三', 'age': '20'});
    if(result.statusCode==200){
      print(json.decode(result.body));      
    }else{
      print(result.statusCode);
    }
  }

2、Dio庫實作網路請求以及動態渲染資料

匯入第三方庫:Dio庫

請求資料

  _getData() async{
      var apiUrl="http://192.168.0.5:3000/news";    
      Response response = await Dio().get(apiUrl);
      print(response.data);
  }

提交資料

 _postData() async{
      Map jsonData={
        "username":"哈哈哈",
        "age":20
      };
      var apiUrl="http://192.168.0.5:3000/dologin";
      Response response = await Dio().post(apiUrl,data:jsonData);
      print(response.data);
  }

Dio Get請求資料、渲染資料

import 'dart:convert';

import 'package:flutter/material.dart';

import 'package:dio/dio.dart';

class HttpDemo extends StatefulWidget {
  HttpDemo({Key key}) : super(key: key);

  _HttpDemoState createState() => _HttpDemoState();
}

class _HttpDemoState extends State<HttpDemo> {
  
  List _list=[];

  @override
  void initState() { 
    super.initState();
    this._getData();
  }

  _getData() async{
    var apiUrl="http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1";
    Response result=await Dio().get(apiUrl);

    // print(json.decode(result.data)["result"]);
    setState(() {
     this._list=json.decode(result.data)["result"]; 
    });  
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("請求資料Dio Demo"),
      ),
      body: this._list.length>0?ListView(
        children: this._list.map((value){
          return ListTile(
            title: Text(value["title"]),
          );
        }).toList(),
      ):Text("加載中...")
    );
  }
}

3、下拉重繪 上拉分頁加載更多

效果圖:

在這里插入圖片描述
在這里插入圖片描述

在這里插入圖片描述

學習之前需要匯入Dio庫,具體用法與匯入連接請查看我的博客
Flutter進階第2篇:Dio庫實作網路請求以及動態渲染資料

News.dart

import 'package:flutter/material.dart';

import 'dart:convert';
import 'package:dio/dio.dart';

class NewsPage extends StatefulWidget {
  NewsPage({Key key}) : super(key: key);

  _NewsPageState createState() => _NewsPageState();
}

class _NewsPageState extends State<NewsPage> {
  List _list = [];
  int _page = 1;
  bool hasMore = true; //判斷有沒有資料
  ScrollController _scrollController = new ScrollController();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getData();

    //監聽滾動條事件
    _scrollController.addListener(() {
      print(_scrollController.position.pixels); //獲取滾動條下拉的距離

      print(_scrollController.position.maxScrollExtent); //獲取整個頁面的高度

      if (_scrollController.position.pixels >
          _scrollController.position.maxScrollExtent - 40) {
        this._getData();
      }
    });
  }

  void _getData() async {
    if (this.hasMore) {
      var apiUrl =
          "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=${_page}";

      var response = await Dio().get(apiUrl);
      var res = json.decode(response.data)["result"];   

      setState(() {
        this._list.addAll(res);  //拼接
        this._page++;
      });
      //判斷是否是最后一頁
      if (res.length < 20) {
        setState(() {
          this.hasMore = false;
        });
      }
    }
  }

  //下拉重繪
  Future<void> _onRefresh() async {
    await Future.delayed(Duration(milliseconds: 2000), () {
      print('請求資料完成');
      _getData();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("新聞串列"),
      ),
      body: this._list.length > 0
          ? RefreshIndicator(
              onRefresh: _onRefresh,
              child: ListView.builder(
                controller: _scrollController,
                itemCount: this._list.length, //20
                itemBuilder: (context, index) {//19                  
                  if (index == this._list.length-1) {   //串列渲染到最后一條的時候加一個圈圈
                    //拉到底
                     return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text("${this._list[index]["title"]}",
                              maxLines: 1),
                        ),
                        Divider(),
                        _getMoreWidget()
                      ],
                    );

                  } else {
                    return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text("${this._list[index]["title"]}",
                              maxLines: 1),
                        ),
                        Divider()
                      ],
                    );
                  }
                },
              ))
          : _getMoreWidget(),
    );
  }

  //加載中的圈圈
  Widget _getMoreWidget() {

    if(hasMore){
      return Center(
        child: Padding(
          padding: EdgeInsets.all(10.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text(
                '加載中...',
                style: TextStyle(fontSize: 16.0),
              ),
              CircularProgressIndicator(
                strokeWidth: 1.0,
              )
            ],
          ),
        ),
      );
    }else{
       return Center(
         child: Text("--我是有底線的--"),
       );
    }
  }
}

4、實作簡單的新聞系統渲染新聞詳情資料以及用flutter_html決議html

效果圖:

點擊這三個新聞串列的內容,即可進入新聞詳情
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

學習之前需要匯入Dio庫,具體用法與匯入連接請查看我的博客
Flutter進階第2篇:Dio庫實作網路請求以及動態渲染資料

還需要匯入決議HTML代碼的第三方庫:
flutter_html

新聞串列代碼:

import 'package:flutter/material.dart';

import 'dart:convert';
import 'package:dio/dio.dart';

class NewsPage extends StatefulWidget {
  NewsPage({Key key}) : super(key: key);

  _NewsPageState createState() => _NewsPageState();
}

class _NewsPageState extends State<NewsPage> {
  List _list = [];
  int _page = 1;
  bool hasMore = true; //判斷有沒有資料
  ScrollController _scrollController = new ScrollController();

  @override
  void initState() {    
    super.initState();
    this._getData();

    //監聽滾動條事件
    _scrollController.addListener(() {
      print(_scrollController.position.pixels); //獲取滾動條下拉的距離

      print(_scrollController.position.maxScrollExtent); //獲取整個頁面的高度

      if (_scrollController.position.pixels >
          _scrollController.position.maxScrollExtent - 40) {
        this._getData();
      }
    });
  }

  void _getData() async {
    if (this.hasMore) {
      var apiUrl =
          "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=${_page}";

      var response = await Dio().get(apiUrl);
      var res = json.decode(response.data)["result"];   
      setState(() {
        this._list.addAll(res);  //拼接
        this._page++;
      });
      //判斷是否是最后一頁
      if (res.length < 20) {
        setState(() {
          this.hasMore = false;
        });
      }
    }
  }

  //下拉重繪
  Future<void> _onRefresh() async {
    await Future.delayed(Duration(milliseconds: 2000), () {
      print('請求資料完成');
      _getData();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("新聞串列"),
      ),
      body: this._list.length > 0
          ? RefreshIndicator(
              onRefresh: _onRefresh,
              child: ListView.builder(
                controller: _scrollController,
                itemCount: this._list.length, //20
                itemBuilder: (context, index) {//19                  
                  if (index == this._list.length-1) {   //串列渲染到最后一條的時候加一個圈圈
                    //拉到底
                     return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text("${this._list[index]["title"]}",
                              maxLines: 1),
                              onTap: (){
                                  Navigator.pushNamed(context, '/newscontent',arguments:{
                                      "aid":this._list[index]["aid"]
                                  });
                              },
                        ),
                        Divider(),
                        _getMoreWidget()
                      ],
                    );

                  } else {
                    return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text("${this._list[index]["title"]}",
                              maxLines: 1),
                          onTap: (){
                              Navigator.pushNamed(context, '/newscontent',arguments:{
                                    "aid":this._list[index]["aid"]
                                });
                          },
                        ),
                        Divider()
                      ],
                    );
                  }
                },
              ))
          : _getMoreWidget(),
    );
  }
  //加載中的圈圈
  Widget _getMoreWidget() {

    if(hasMore){
      return Center(
        child: Padding(
          padding: EdgeInsets.all(10.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text(
                '加載中...',
                style: TextStyle(fontSize: 16.0),
              ),
              CircularProgressIndicator(
                strokeWidth: 1.0,
              )
            ],
          ),
        ),
      );
    }else{
       return Center(
         child: Text("--我是有底線的--"),
       );
    }
  }
}

新聞詳情

import 'package:flutter/material.dart';

import 'dart:convert';
import 'package:dio/dio.dart';

import 'package:flutter_html/flutter_html.dart';


class NewsContent extends StatefulWidget {

  Map arguments;
  NewsContent({Key key,this.arguments}) : super(key: key);

  _NewsContentState createState() => _NewsContentState(this.arguments);
}

class _NewsContentState extends State<NewsContent> {
  Map arguments;
  List _list=[];
  
  _NewsContentState(this.arguments);

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    print(this.arguments);

    this._getData();
  }

  _getData() async{
    var apiUrl="http://www.phonegap100.com/appapi.php?a=getPortalArticle&aid=${this.arguments["aid"]}";
    var response=await Dio().get(apiUrl);     

    setState(() {
      this._list=json.decode(response.data)["result"];
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("新聞詳情")
      ),
      body:ListView(
        children: <Widget>[
          // Text("${this._list.length>0?this._list[0]["title"]:''}"),
          // Text("${this._list.length>0?this._list[0]["content"]:''}")

              Html(
              data: """
              ${this._list.length>0?this._list[0]["content"]:''}                            
              """,
              //Optional parameters:
              padding: EdgeInsets.all(8.0),
              backgroundColor: Colors.white70,
              defaultTextStyle: TextStyle(fontFamily: 'serif'),
              linkStyle: const TextStyle(
                color: Colors.redAccent,
              ),
              onLinkTap: (url) {
                // open url in a webview
              }
            )
        ],
      )
    );
  }
}

記得配置路由時,要傳值:
在這里插入圖片描述

5、使用WebView組件flutter_inappbrowser加載遠程web頁面渲染新聞詳情資料

效果圖

點擊這三個新聞串列的內容,即可進入新聞詳情
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

匯入第三方庫:flutter_inappbrowser

News.dart 新聞串列

import 'package:flutter/material.dart';

import 'dart:convert';
import 'package:dio/dio.dart';

class NewsPage extends StatefulWidget {
  NewsPage({Key key}) : super(key: key);

  _NewsPageState createState() => _NewsPageState();
}

class _NewsPageState extends State<NewsPage> {
  List _list = [];
  int _page = 1;
  bool hasMore = true; //判斷有沒有資料
  ScrollController _scrollController = new ScrollController();

  @override
  void initState() {    
    super.initState();
    this._getData();

    //監聽滾動條事件
    _scrollController.addListener(() {
      print(_scrollController.position.pixels); //獲取滾動條下拉的距離

      print(_scrollController.position.maxScrollExtent); //獲取整個頁面的高度

      if (_scrollController.position.pixels >
          _scrollController.position.maxScrollExtent - 40) {
        this._getData();
      }
    });
  }

  void _getData() async {
    if (this.hasMore) {
      var apiUrl =
          "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=${_page}";

      var response = await Dio().get(apiUrl);
      var res = json.decode(response.data)["result"];   
      setState(() {
        this._list.addAll(res);  //拼接
        this._page++;
      });
      //判斷是否是最后一頁
      if (res.length < 20) {
        setState(() {
          this.hasMore = false;
        });
      }
    }
  }

  //下拉重繪
  Future<void> _onRefresh() async {
    await Future.delayed(Duration(milliseconds: 2000), () {
      print('請求資料完成');
      _getData();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("新聞串列"),
      ),
      body: this._list.length > 0
          ? RefreshIndicator(
              onRefresh: _onRefresh,
              child: ListView.builder(
                controller: _scrollController,
                itemCount: this._list.length, //20
                itemBuilder: (context, index) {//19                  
                  if (index == this._list.length-1) {   //串列渲染到最后一條的時候加一個圈圈
                    //拉到底
                     return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text("${this._list[index]["title"]}",
                              maxLines: 1),
                              onTap: (){
                                  Navigator.pushNamed(context, '/newscontent',arguments:{
                                      "aid":this._list[index]["aid"]
                                  });
                              },
                        ),
                        Divider(),
                        _getMoreWidget()
                      ],
                    );

                  } else {
                    return Column(
                      children: <Widget>[
                        ListTile(
                          title: Text("${this._list[index]["title"]}",
                              maxLines: 1),
                          onTap: (){
                              Navigator.pushNamed(context, '/newscontent',arguments:{
                                    "aid":this._list[index]["aid"]
                                });
                          },
                        ),
                        Divider()
                      ],
                    );
                  }
                },
              ))
          : _getMoreWidget(),
    );
  }
  //加載中的圈圈
  Widget _getMoreWidget() {

    if(hasMore){
      return Center(
        child: Padding(
          padding: EdgeInsets.all(10.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text(
                '加載中...',
                style: TextStyle(fontSize: 16.0),
              ),
              CircularProgressIndicator(
                strokeWidth: 1.0,
              )
            ],
          ),
        ),
      );
    }else{
       return Center(
         child: Text("--我是有底線的--"),
       );
    }
  }
}

NewsContent.dart 新聞詳情


import 'package:flutter/material.dart';

import 'package:flutter_inappbrowser/flutter_inappbrowser.dart';


class NewsContent extends StatefulWidget {

  Map arguments;
  NewsContent({Key key,this.arguments}) : super(key: key);

  _NewsContentState createState() => _NewsContentState(this.arguments);
}

class _NewsContentState extends State<NewsContent> {
  Map arguments;
  bool _flag=true;

  _NewsContentState(this.arguments);
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    print(this.arguments);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("新聞詳情")
      ),
      body:Column(
        children: <Widget>[
          this._flag?_getMoreWidget():Text(""),
          Expanded(
            child: InAppWebView(
                    initialUrl: "http://www.phonegap100.com/newscontent.php?aid=${this.arguments["aid"]}",                   
                    onProgressChanged: (InAppWebViewController controller, int progress) {
                        print(progress/100);
                        if((progress/100)>0.999){
                          setState(() {
                             this._flag=false;
                          });
                        }
                    },
            ),     
          )
        ],
      )
    );
  }
  //加載中的圈圈
  Widget _getMoreWidget() {
      return Center(
        child: Padding(
          padding: EdgeInsets.all(10.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text(
                '加載中...',
                style: TextStyle(fontSize: 16.0),
              ),
              CircularProgressIndicator(
                strokeWidth: 1.0,
              )
            ],
          ),
        ),
      );    
  }
}

記得配置路由時,要傳值:
在這里插入圖片描述

注意事項:
SDK版本一定要大于等于17
在這里插入圖片描述

6、獲取設備資訊 以及 使用高德Api獲取地理位置

效果圖:

在這里插入圖片描述

在這里插入圖片描述
引入第三方庫:device_info

Device.dart

import 'package:flutter/material.dart';
import 'package:device_info/device_info.dart';

class DevicePage extends StatefulWidget {
  DevicePage({Key key}) : super(key: key);

  _DevicePageState createState() => _DevicePageState();
}

class _DevicePageState extends State<DevicePage> {
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    this._getDevice();
  }
  _getDevice() async{
    DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
    AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
    print('設備號 ${androidInfo.androidId}');  // e.g. "Moto G (4)"
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter Native Device演示"),
      ),
      body: Text("看控制臺 資訊已經列印到控制臺了"),
    );
  }
}

第一步:使用高德定位準備作業獲取 key

1、申請成為開發者
2、創建應用配置獲取Key

點擊查看教程

第二步:引入第三方庫

amap_location

第三步:修改 你的專案目錄e /app/build.gradle 在 在 g android/defaultConfig 節點修 改 改 manifestPlaceholders, 新增高德地圖 y key 配置

android {
.... 你的代碼
defaultConfig {
.....
manifestPlaceholders = [
AMAP_KEY : "aa9f0cf8574400f2af0078392c556e25", // 高德
地圖 key
]
}
...你的代碼
dependencies {
/// 注意這里需要在主專案增加一條依賴,否則可能發生編譯不通過的
情況
implementation 'com.amap.api:location:latest.integration'
...你的代碼
}

在這里插入圖片描述

7、呼叫原生硬體Api實作照相機拍照和相冊選擇 以及拍照上傳到服務器

效果圖:
在這里插入圖片描述
相冊

在這里插入圖片描述
拍照

在這里插入圖片描述

拍照后的照片顯示在界面上

在這里插入圖片描述

7.1丶 呼叫原生硬體Api實作照相機拍照和相冊選擇

匯入第三方庫:image_picker

拍照

_takePhoto() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
_imgPath = image;
});
}

相冊

_openGallery() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_imgPath = image;
});
}

7.2丶 拍照上傳到服務器

匯入第三方庫:上傳圖片到服務器

上傳圖片代碼

_uploadData(imageFile) async{
FormData formData = new FormData.from({
"name": "wendux",
"age": 25,
'file': new UploadFileInfo(imageFile, "imageFileName.jpg")
});
var response = await Dio().post("http://jd.itying.com/imgupload", data: formData);
print(response);
}

完整代碼:

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:dio/dio.dart';
class ImagePickerPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ImagePickerState();
}
}
class _ImagePickerState extends State<ImagePickerPage> {
var _imgPath;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ImagePicker"),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_ImageView(_imgPath),
RaisedButton(
onPressed: _takePhoto,
child: Text("拍照"),
),
RaisedButton(
onPressed: _openGallery,
child: Text("選擇照片"),
),
],
),
));
}
/*圖片控制元件*/
Widget _ImageView(imgPath) {
if (imgPath == null) {
return Center(
child: Text("請選擇圖片或拍照"),
);
} else {
return Image.file(
imgPath,
);
}
}
/*拍照*/
_takePhoto() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera,maxWidth: 400);
_uploadData(image); //上傳
setState(() {
_imgPath = image;
});
}
/*相冊*/
_openGallery() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_imgPath = image;
});
}
_uploadData(imageFile) async{
FormData formData = new FormData.from({
"name": "wendux",
"age": 25,
'file': new UploadFileInfo(imageFile, "imageFileName.jpg")
});
var response = await Dio().post("http://jd.itying.com/imgupload", data: formData);
print(response);
}
}

8、實作視頻播放

效果圖:

在這里插入圖片描述

引入第三方庫:chewie

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';


class ChewieVideoDemo extends StatefulWidget {
  ChewieVideoDemo({Key key}) : super(key: key);

  _ChewieVideoDemoState createState() => _ChewieVideoDemoState();
}

class _ChewieVideoDemoState extends State<ChewieVideoDemo> {

  VideoPlayerController videoPlayerController;
  ChewieController chewieController;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    videoPlayerController = VideoPlayerController.network(
    'http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4');

    chewieController = ChewieController(
      videoPlayerController: videoPlayerController,
      aspectRatio: 3 / 2,
      autoPlay: true,
      looping: true,
    );
  }
  /*銷毀*/
  @override
  void dispose() {
    videoPlayerController.dispose();
    chewieController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('在線視頻播放'),
      ),
      body: Center(
        child: Chewie(
          controller: chewieController,
        )
      ),
    );
  }
}

9、檢測網路連接,監聽網路變化

效果圖:

在這里插入圖片描述
在這里插入圖片描述

在這里插入圖片描述
引入第三方庫:connectivity

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';


class ChewieVideoDemo extends StatefulWidget {
  ChewieVideoDemo({Key key}) : super(key: key);

  _ChewieVideoDemoState createState() => _ChewieVideoDemoState();
}

class _ChewieVideoDemoState extends State<ChewieVideoDemo> {

  VideoPlayerController videoPlayerController;
  ChewieController chewieController;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    videoPlayerController = VideoPlayerController.network(
    'http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4');

    chewieController = ChewieController(
      videoPlayerController: videoPlayerController,
      aspectRatio: 3 / 2,
      autoPlay: true,
      looping: true,
    );
  }
  
  /*銷毀*/
  @override
  void dispose() {
    videoPlayerController.dispose();
    chewieController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('在線視頻播放'),
      ),
      body: Center(
        child: Chewie(
          controller: chewieController,
        )
      ),
    );
  }
}

10、 本地存盤,封裝本地存盤類,實作最簡單的狀態管理

引入第三方庫:shared_preferences

設定值:

SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString(key, value);
prefs.setBool(key, value)
prefs.setDouble(key, value)
prefs.setInt(key, value)
prefs.setStringList(key, value)

獲取值:

SharedPreferences prefs = await SharedPreferences.getInstance();
var data=prefs.getString("name");

洗掉值:

SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove(key); //洗掉指定鍵
prefs.clear();//清空鍵值對

完整代碼:

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class StoragePage extends StatefulWidget {
  StoragePage({Key key}) : super(key: key);

  _StoragePageState createState() => _StoragePageState();
}

class _StoragePageState extends State<StoragePage> {

  _saveData() async{
      SharedPreferences sp=await SharedPreferences.getInstance();

      sp.setString("username", "張三111");

       sp.setString("age", "26");
       
  }
  _getData() async{
      SharedPreferences sp=await SharedPreferences.getInstance();

      print(sp.getString("username"));

      print(sp.getString("age"));
  }
  _removeData() async{
     SharedPreferences sp=await SharedPreferences.getInstance();

      print(sp.remove("age"));

  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("本地存盤"),
      ),
      body: Center(
        child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
          RaisedButton(
            child: Text('保存資料'),
            onPressed: _saveData,
          ),
          SizedBox(height: 10),
          RaisedButton(
            child: Text('獲取資料'),
            onPressed:_getData,
          ),
           SizedBox(height: 10),
          RaisedButton(
            child: Text('清除資料'),
            onPressed:_removeData,
          )         
        ]),
      ),
    );
  }
}

簡單封裝:

import 'package:shared_preferences/shared_preferences.dart';

class Storage{

  static Future<void> setString(key,value) async{
       SharedPreferences sp=await SharedPreferences.getInstance();
       sp.setString(key, value);
  }

  static Future<String> getString(key) async{
       SharedPreferences sp=await SharedPreferences.getInstance();
       return sp.getString(key);
  }

  static Future<void> remove(key) async{
       SharedPreferences sp=await SharedPreferences.getInstance();
       sp.remove(key);
  }
}

11、呼叫原生硬體Api實作掃碼 掃描條形碼 掃描二維碼

效果圖:

在這里插入圖片描述
在這里插入圖片描述

在這里插入圖片描述

第一步:

匯入第三方庫:barcode_scan

第二步:

<uses-permission android:name="android.permission.CAMERA" />

第三步:

3.1 的 編 輯 你 的 d android 的 目 錄 下 面 的 e build.gradle ( Edit your project-level
build.gradle file to look like this)
注意: : 官方檔案配置的 kotlin_version 的版本是 1.2.31,但是實際發現 1.2.31
會報錯,所以本專案使用 1.3.0,

buildscript {
ext.kotlin_version = '1.3.0'
...
dependencies {
...
classpath
"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

第四步:編輯你 的 p android/app 的 目 錄下 面 的 e build.gradle ( Edit your app-levelbuild.gradle file to look like this)

apply plugin: 'kotlin-android'
...
dependencies {
implementation
"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
...
}
import 'package:flutter/material.dart';

import 'package:barcode_scan/barcode_scan.dart';
import 'package:flutter/services.dart';

class ScanPage extends StatefulWidget {
  ScanPage({Key key}) : super(key: key);

  _ScanPageState createState() => _ScanPageState();
}

class _ScanPageState extends State<ScanPage> {
  String barcode;

  Future _scan() async {
    try {
      String barcode = await BarcodeScanner.scan();
      setState(() {
        return this.barcode = barcode;
      });
    } on PlatformException catch (e) {
      if (e.code == BarcodeScanner.CameraAccessDenied) {
        setState(() {
          return this.barcode = 'The user did not grant the camera permission!';
        });
      } else {
        setState(() {
          return this.barcode = 'Unknown error: $e';
        });
      }
    } on FormatException {
      setState(() => this.barcode =
          'null (User returned using the "back"-button before scanning anything. Result)');
    } catch (e) {
      setState(() => this.barcode = 'Unknown error: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.photo_camera),
          onPressed: _scan,
        ),
        appBar: AppBar(
          title: Text("掃碼"),
        ),
        body: Text("${barcode}")
    );
  }
}

12、檢測應用版本號、服務器下載檔案以及實作App自動升級、安裝

12.1丶 配置版本號:

<manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="0.0.1"
package="io.jdshop.demo" xmlns:android="http://schemas.android.com/apk/res/android">

12.2丶 升級 app 之前的準備作業配置權限
配置 AndroidMenifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

12.3丶 Android 升級 app 涉及的 API 庫

在這里插入圖片描述

12.4、獲取版本資訊

https://pub.dev/packages/package_info

PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appName = packageInfo.appName;
String packageName = packageInfo.packageName;
String version = packageInfo.version;
String buildNumber = packageInfo.buildNumber;

print("appName:${appName}");
print("packageName:${packageName}");
print("version:${version}");
print("buildNumber:${buildNumber}");

12.5、獲取檔案存盤路徑.

https://pub.dev/packages/path_provider

Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;

Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;

var directory = await getExternalStorageDirectory();

String storageDirectory=directory.path;

print("tempPath:${tempPath}");

print("appDocDir:${appDocPath}");

print("StorageDirectory:${storageDirectory}");

12.6、下載檔案

https://pub.dev/packages/flutter_downloader

final directory = await getExternalStorageDirectory();
String _localPath = directory.path;
final taskId = await FlutterDownloader.enqueue(

url: "http://www.ionic.wang/jdshop.apk",
savedDir: _localPath,
showNotification:
true, // show download progress in status bar (for Android)
openFileFromNotification:
true, // click on notification to open downloaded file (for Android)
);

12.7、打開檔案

https://pub.dev/packages/open_file

OpenFile.open("${_localPath}/jdshop.apk");

12.8、注意事項

1、服務器的 App 版本必須大于本地 App 版本
2、本地 App 和服務器 App 的包名稱 簽名必須一致,這樣的話服務器的包才可以替換本地
的包,

13、打開外部瀏覽器、打開外部應用、撥打電話、發送短信

效果圖:

在這里插入圖片描述

打開外部瀏覽器
在這里插入圖片描述
發送短信
在這里插入圖片描述
撥打電話
在這里插入圖片描述
打開外部應用
在這里插入圖片描述

匯入第三方庫:url_launcher

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

class UrlLauncher extends StatefulWidget {
  UrlLauncher({Key key}) : super(key: key);

  _UrlLauncherState createState() => _UrlLauncherState();
}

class _UrlLauncherState extends State<UrlLauncher> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('UrlLauncher'),
        ),
        body: Center(
            child: Padding(
          padding: EdgeInsets.all(20),
          child: ListView(children: [
            RaisedButton(
              child: Text('打開外部瀏覽器'),
              onPressed: () async{                                 
                  const url = 'https://cflutter.com';
                  if (await canLaunch(url)) {
                    await launch(url);
                  } else {
                    throw 'Could not launch $url';
                  }
              },
            ),
            SizedBox(height: 10),
            RaisedButton(
              child: Text('撥打電話'),
              onPressed: () async{
                  var tel = 'tel:10086';
                  if (await canLaunch(tel)) {
                    await launch(tel);
                  } else {
                    throw 'Could not launch $tel';
                  }
                
              },
            ),
            SizedBox(height: 10),
            RaisedButton(
              child: Text('發送短信'),
              onPressed: () async{
                 var tel = 'sms:10086';
                  if (await canLaunch(tel)) {
                    await launch(tel);
                  } else {
                    throw 'Could not launch $tel';
                  }
              },
            ),
            SizedBox(height: 10),
            RaisedButton(
              child: Text('打開外部應用'),
              onPressed: () async{
                  /*
                    weixin://
                    alipays://
                  */
                  var url = 'alipays://';	//支付寶的 scheme碼
                  if (await canLaunch(url)) {
                    await launch(url);
                  } else {
                    throw 'Could not launch $url';
                  }
                  
              },
            )       
          ]),
        )));
  }
}

打開其他APP的scheme碼:
https://www.cflutter.com/topic/5d0853733b57e317a4d0af01

14、支付寶支付【上】

一丶 準備作業、接入支付寶:

1. 必須注冊企業支付寶賬戶,如果已有企業支付寶賬戶忽略此步驟
2. 百度搜索《支付寶開放平臺》,或者點擊下面鏈接進入支付寶開發接入頁面:
https://open.alipay.com/developmentAccess/developmentAccess.htm

在這里插入圖片描述
3. 點擊支付應用

在這里插入圖片描述

4. 填寫對應應用名稱 圖示 點擊創建:

在這里插入圖片描述

官方答復:應用型別分為兩大類:第三方應用、自用型應用 第三方應用:適用于服務商,
為商戶開發應用,拓展商戶使用,詳見供他人使用;目前僅支持小程式的三方接入,接入小
程式前,必須先申請小程式的公測; 自用型應用:使用開放的功能,為自己或自己公司開
發應用,詳見自己使用;自研型應用分為網頁/移動,AR(僅企業支付寶),小程式(僅企
業支付寶),生活號
網頁&移動應用 和 第三方應用的區別是一個是自己使用,一個是幫助第三方去簽約相關產

5.設定應用公鑰,然后提交審核, 如何創建公鑰繼續往后看…
在這里插入圖片描述

二丶 介面加密簽名

  1. 下載簽名工具,
    https://docs.open.alipay.com/291/106097/

2.簽名,并保存好私鑰、公鑰

在這里插入圖片描述
在這里插入圖片描述

三丶 配置簽名、提交審核

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
設定完成以后支付寶后臺也會給我們生成一個公鑰,注意兩個不一樣

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

四丶 安裝支付流程

官方支付流程檔案:https://docs.open.alipay.com/59/103658/

15、支付寶支付【下】

一丶 支付寶客戶端支付流程

官方支付流程檔案:https://docs.open.alipay.com/59/103658/

二丶 準備已有的 Flutter 專案安裝插件

https://pub.dev/packages/sy_flutter_alipay

三丶服務器端呼叫支付寶 sdk 生成訂單資訊

  1. 服務端 sdk 下載地址:https://docs.open.alipay.com/54/103419/
  2. 本教程采用的 php 的 sdk,看演示

四丶 客戶端呼叫服務器端介面生成訂單簽名資訊,呼叫支付插件完成支付

import 'package:flutter/material.dart';
import 'package:sy_flutter_alipay/sy_flutter_alipay.dart';
import 'package:dio/dio.dart';
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
_doPay() async{
var apiUrl='http://agent.itying.com/alipay/index.php';
var myPayInfo =await Dio().get(apiUrl);
final payInfo =myPayInfo.data;
print(payInfo);

var result = await SyFlutterAlipay.pay(
payInfo,
// urlScheme: '你的 ios urlScheme', //前面配置的 urlScheme
// isSandbox: true //是否是沙箱環境,只對 android 有效
);
print(result);
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 20),
RaisedButton(
child: Text('支付寶支付'),
onPressed: _doPay,
),
SizedBox(height: 20),
],
),
);
}
}

五丶 服務器端異步回呼更新訂單資訊

當支付成功后支付寶會異步給服務器 post 資料,服務器更新訂單資訊
在這里插入圖片描述
服務端 sdk 下載地址:https://docs.open.alipay.com/54/103419/
本教程采用的 php 的 sdk,看演示

六丶 Flutter 在 Xcode 上編譯提示:Target ‘Runner’: script phase “[CP] Embed Pods Frameworks” 的解決辦法
在這里插入圖片描述
解決方法:https://www.cflutter.com/topic/5d09a1c73b57e317a4d0af08

16、ListView嵌套GridView、不同終端螢屏適配方案

一丶 豎向 ListView 嵌套橫向 ListView ,以 及ListView 嵌套 GridView

1 、 豎向 ListView 嵌套橫向 ListView 注意事項:
在豎向 ListView 中嵌套橫向 ListView 的時候要注意給橫向 ListView 外層加一個容器,然后外
層這個容器要設定高度,外層這個容器可以是 SizedBox ,也可以是 Container,

2 、ListView 嵌套 GridView 注意事項:
由于 GridView 和 ListView 都是可以滾動的組件,所以嵌套的時候要注意把里面的組件改為不可滾動組件,
重要屬性:
shrinkWrap: true, //解決無限高度問題
physics:NeverScrollableScrollPhysics(), //禁用滑動事件

二丶 不同終端螢屏適配方案

匯入第三方庫: flutter_screenutil

import 'package:flutter/material.dart';

class ScreenUtil {
  static ScreenUtil instance = new ScreenUtil();
  
//設計稿的設備尺寸修改
  double width;
  double height;
  bool allowFontScaling;
  
  static MediaQueryData _mediaQueryData;
  static double _screenWidth;
  static double _screenHeight;
  static double _pixelRatio;
  static double _statusBarHeight;
  
  static double _bottomBarHeight;
  
  static double _textScaleFactor;
  
  ScreenUtil({
    this.width = 1080,
    this.height = 1920,
    this.allowFontScaling = false,
  });
  
  static ScreenUtil getInstance() {
    return instance;
  }
  
  void init(BuildContext context) {
    MediaQueryData mediaQuery = MediaQuery.of(context);
    _mediaQueryData = mediaQuery;
    _pixelRatio = mediaQuery.devicePixelRatio;
    _screenWidth = mediaQuery.size.width;
    _screenHeight = mediaQuery.size.height;
    _statusBarHeight = mediaQuery.padding.top;
    _bottomBarHeight = _mediaQueryData.padding.bottom;
    _textScaleFactor = mediaQuery.textScaleFactor;
  }
  
  static MediaQueryData get mediaQueryData => _mediaQueryData;
  
  ///每個邏輯像素的字體像素數,字體的縮放比例
  static double get textScaleFactory => _textScaleFactor;
  
  ///設備的像素密度
  static double get pixelRatio => _pixelRatio;
  
  ///當前設備寬度 dp
  static double get screenWidthDp => _screenWidth;
  
  ///當前設備高度 dp
  static double get screenHeightDp => _screenHeight;
  
  ///當前設備寬度 px
  static double get screenWidth => _screenWidth * _pixelRatio;
  
  ///當前設備高度 px
  static double get screenHeight => _screenHeight * _pixelRatio;
  
  ///狀態欄高度 dp 劉海屏會更高
  static double get statusBarHeight => _statusBarHeight;
  
  ///底部安全區距離 dp
  static double get bottomBarHeight => _bottomBarHeight;
  
  ///實際的 dp 與設計稿 px 的比例
  get scaleWidth => _screenWidth / instance.width;
  get scaleHeight => _screenHeight / instance.height;

  ///根據設計稿的設備寬度適配
  ///高度也根據這個來做適配可以保證不變形
  setWidth(double width) => width * scaleWidth;
  
  /// 根據設計稿的設備高度適配
  /// 當發現設計稿中的一屏顯示的與當前樣式效果不符合時,
  /// 或者形狀有差異時,高度適配建議使用此方法
  /// 高度適配主要針對想根據設計稿的一屏展示一樣的效果
  setHeight(double height) => height * scaleHeight;
  
  ///字體大小適配方法
  ///@param fontSize 傳入設計稿上字體的 px ,
  ///@param allowFontScaling 控制字體是否要根據系統的“字體大小”輔助選項來進行縮
  放,默認值為 false///@param allowFontScaling Specifies whether fonts should scale to respect Text Size
  accessibility settings. The default is false.
  setSp(double fontSize) => allowFontScaling
      ? setWidth(fontSize)
      : setWidth(fontSize) / _textScaleFactor;
}

17、JSON的序列化和反序列化、創建模型類轉換Json資料

一、Flutter JSON 序列化反序列化

1、使用 dart:convert 手動序列化 JSON
2、模型類中序列化 JSON
小專案中使用 dart:convert 手動序列化 JSON 非常好,也非常快速,但是隨著專案的增大,
dart:convert 手動序列化 JSON 的話失去了大部分靜態型別語言特性:型別安全、自動補全和
最重要的編譯時例外,這樣一來,我們的代碼可能會變得非常容易出錯,
當我們訪問 name 或 email 欄位時,我們輸入的很快,導致欄位名打錯了,但由于這個 JSON
在 map 結構中,所以編譯器不知道這個錯誤的欄位名,
為了解決上面的問題在大型專案中使用的更多的是在模型類中序列化 JSON,

二、 Flutter JSON 字串和 Map 換 型別的轉換 dart:convert手動序列化 JSON

import 'dart:convert'
var mapData={"name":"張三","age":"20"};
var strData='{"name":"張三","age":"20"}';
print(json.encode(mapData)); //Map 轉換成 Json 字串
print(json.decode(strData)); //Json 字串轉化成 Map 型別

三丶 Flutter 在模型類中序列化 JSON

class FocusModel {
  String sId;
  String title;
  String status;
  String pic;
  String url;
  FocusModel ({this.sId, this.title, this.status, this.pic, this.url});
  FocusModel .fromJson(Map<String, dynamic> json) {
    sId = json['_id'];
    title = json['title'];
    status = json['status'];
    pic = json['pic'];
    url = json['url'];
  }
  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['_id'] = this.sId;
    data['title'] = this.title;
    data['status'] = this.status;
    data['pic'] = this.pic;
    data['url'] = this.url;
    return data;
  }
}
var strData='{"_id":"59f6ef443ce1fb0fb02c7a43","title":"筆記本電腦
","status":"1","pic":"public\\upload\\UObZahqPYzFvx_C9CQjU8KiX.png","
url":"12"
}';
var data= FocusModel.fromJson( strData )

可參考: https://flutterchina.club/json/

四丶 json_to_dart 自動生成模型類

https://javiercbk.github.io/json_to_dart/

18、底部 Tab 切換保持頁面狀態的幾種方法

一、IndexedStack 保持頁面狀態

IndexedStack 和 Stack 一樣,都是層布局控制元件, 可以在一個控制元件上面放置另一
個控制元件,但唯一不同的是 IndexedStack 在同一時刻只能顯示子控制元件中的一個控
件,通過 Index 屬性來設定顯示的控制元件,
IndexedStack 來保持頁面狀態的優點就是配置簡單,IndexedStack 保持頁面狀
態的缺點就是不方便單獨控制每個頁面的狀態,
k IndexedStack 用法:

Container(
width: double.infinity,
height: double.infinity,
child: new IndexedStack(
index: 0,
alignment: Alignment.center,
children: <Widget>[
Image.network("https://www.itying.com/images/flutter/list1.jpg",fit:
BoxFit.cover,),
Image.network("https://www.itying.com/images/flutter/list2.jpg",fit:
BoxFit.cover)
],
),
)

結合底部 tab 切換

body:IndexedStack(
children: this._pageList,
index: _currentIndex,
),

二丶 AutomaticKeepAliveClientMixin 保持頁面狀態
AutomaticKeepAliveClientMixin 結合 tab 切換保持頁面狀態相比 IndexedStack 而言配置起來稍
微有些復雜,它結合底部 BottomNavigationBar 保持頁面狀態的時候需要進行如下配置,

import 'package:flutter/material.dart';
import 'Home.dart';
import 'Category.dart';
import 'Cart.dart';
import 'User.dart';
class Tabs extends StatefulWidget {
  Tabs({Key key}) : super(key: key);
  _TabsState createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
  int _currentIndex=1;
//創建頁面控制器
  var _pageController;
  @override
  void initState(){
//頁面控制器初始化
    _pageController = new PageController(initialPage : _currentIndex);
    super.initState();
  }

  List<Widget> _pageList=[
    HomePage(),
    CategoryPage(),
    CartPage(),
    UserPage()
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("jdshop"),
        ),
//必須用 PageView 加載不同的頁面
        body: PageView(
          controller: _pageController,
          children: this._pageList,
          onPageChanged: (index){
            _currentIndex = index;
          },
        ),
        bottomNavigationBar: BottomNavigationBar(
            currentIndex:this._currentIndex ,
            onTap: (index){
              setState(() {
//this._currentIndex=index;
//頁面控制器進行跳轉
                _pageController.jumpToPage(this._currentIndex);
              });
            },
            type:BottomNavigationBarType.fixed ,
            fixedColor:Colors.red,
            items: [
        BottomNavigationBarItem(
        icon: Icon(Icons.home),
        title: Text("首頁")
    ),
    BottomNavigationBarItem(
    icon: Icon(Icons.category),
    title: Text("分類")
    ),
    BottomNavigationBarItem(
        icon: Icon(Icons.shopping_cart),
        title: Text("購物車")
    ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.people),
                  title: Text("我的")
              )
            ],
        ),
    );
  }
}

需要持久化的頁面加入如下代碼:

class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);
  _HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin{
  @override
  bool get wantKeepAlive => true;
}

19、inappbrowser、StatefulBuilder 更新 Flutter showDialog、showModalBottomSheet中的狀態

一丶 Flutter WebView 組件 inappbrowser的使用
https://pub.dev/packages/flutter_inappbrowser

注意事項:

在這里插入圖片描述

二丶 StatefulBuilder 更新 Flutter showDialog、showModalBottomSheet中的狀態

參考:https://www.cflutter.com/topic/5d202202403aa10564178c65

20、官方推薦的狀態管理庫 provider 的使用

一丶 狀態管理

通俗的講:當我們想在多個頁面(組件/Widget)之間共享狀態(資料),或者一個頁面(組
件/Widget)中的多個子組件之間共享狀態(資料),這個時候我們就可以用 Flutter 中的狀
態管理來管理統一的狀態(資料),實作不同組件直接的傳值和資料共享,
現在 Flutter 的狀態管理方案很多,redux、bloc、state、provide、provider,
目前我們推薦使用 provider,這個是官方提供的狀態管理解決方案,相比其他狀態管理庫使
用起來比較方便,

二、 關于 于flutter provider 庫和 和 flutter provide 庫

provider 是 Flutter 團隊推出的狀態管理模式,
官方地址為: https://pub.dev/packages/provider
注意:p rovider 和 provide 是兩個庫哦,Flutter 官方推薦使用的是 provider 哦,provider 是
flutter 官方出的,provide 不是 Flutter 官方寫的哦

三丶 flutter provider 的使用

1、配置依賴 provider: ^3.0.0+1
2、新建一個檔案夾叫 provider,在 provider 檔案夾里面放我們對于的狀態管理類
3、在 provider 里面新建 Counter.dart
4、Counter.dart 里面新建一個類繼承 minxins 的 ChangeNotifier 代碼如下

import 'package:flutter/material.dart';
class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

5、找到 main.dart 修改代碼如下

import 'package:flutter/material.dart';
import 'routers/router.dart';
import 'package:provider/provider.dart';
import 'provider/Counter.dart';
void main() =>runApp(MyApp());
// void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
// Provider<Counter>.value(value: foo),
        ChangeNotifierProvider(builder: (_) => Counter()),
      ],
      child: MaterialApp(
// home: Tabs(),
        debugShowCheckedModeBanner: false,
        initialRoute: '/productContent',
        onGenerateRoute: onGenerateRoute,
        theme: ThemeData(
// primaryColor: Colors.yellow
            primaryColor: Colors.white),
      ),
    );
  }
}

6、獲取值、以及設定值

import 'package:provider/provider.dart';
import '../../provider/Counter.dart';
Widget build(BuildContext context) {
  final counter = Provider.of<Counter>(context);
// counter.init();
  return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: (){
          counter.increment();
        },
      ),
      body: Text("counter 的值:${counter.count}")
  );
}

21、事件廣播 、事件監聽

一丶 event_bus 介紹

在前面的課程我們給大家講過狀態管理 Provider 的使用,
通俗的講狀態管理就是:當我們想在多個頁面(組件/Widget)之間共享狀態(資料),或
者一個頁面(組件/Widget)中的多個子組件之間共享狀態(資料),這個時候我們就可以
用 Flutter 中的狀態管理來管理統一的狀態(資料),實作不同組件直接的傳值和資料共享,
那么這一講給大家講的 event_bus 主要是實作不同組件之間的資料傳值,以及在一個組件中
執行另一個組件的方法,

二丶 event_bus 使用事件廣播 、事件監聽

https://pub.dev/packages/event_bus

1 、配置安裝依賴
event_bus: ^1.1.0

2 、 新建一檔案 EventBus.dart 配置如下代碼

import 'package:event_bus/event_bus.dart';
//Bus 初始化
EventBus eventBus = EventBus();
class ProductContentEvent {
  String text;
  ProductContentEvent(String text){
    this.text = text;
  }
}

3 、 在需要廣播事件的頁面引入上面的 EventBus.dart類 然后配置如下代碼

eventBus.fire(new ProductContentEvent('購物車'));

4 、 在需要監聽廣播的地方引入上面的 EventBus.dart 類,然后配置如下代碼

void initState() {
  super.initState();
//監聽廣播
  eventBus.on<ProductContentEvent>().listen((event){
    print(event);
    this._attrBottomSheet();
  });
}

三丶 event_bus 取消事件監聽

var actionSubscription =eventBus.on<ProductContentEvent>().listen((event){
  print(event);
  this._attrBottomSheet();
});
actionSubscription.cancel();

22、點擊穿透問題、頁面禁止左右滑動

一丶 點擊穿透問題

HitTestBehavior.deferToChild 只有有子 Widget 通過了 Hit-Test,才接收一系列的事件,接收
區域也會被限制在該子 Widget 區域中,
HitTestBehavior.opaque 能夠通過 Hit-Test,接收事件,且能阻止在它之前的 Widget(直觀
來看就是被它擋住的 Widget)接收事件,簡單來說就是事件 不能透傳,
HitTestBehavior.translucent 能夠通過 Hit-Test,接收事件,且不會阻止它之前的 Widget(直
觀來看就是被它擋住的 Widget)接收事件,簡單來說就是事件 能透傳,

GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
return false;
}
)

二丶 頁面禁止左右滑動

部分真機詳情左右滑動太靈敏 導致詳情上下滑動不好滑動,這個時候也可禁用詳情Tab滑動

physics: NeverScrollableScrollPhysics(), //禁止 pageView 滑動

喜歡記得點個贊喲,我是王睿,很高興認識大家!

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/291070.html

標籤:其他

上一篇:Android 將專案中的某Module打包成aar

下一篇:【鴻蒙】鴻蒙App應用-《校園通》開發步驟

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more