flutter各類功能實現方法及比較(更新中......)

  • 實現widget隱藏

方法

  1. 使用Opacity
bool _visiable = false;
Opacity(
  opacity: _visiable ? 1.0 : 0.0,
  child: const Text('Now you see me, now you don\'t!'), ) 複製代碼

隱藏後仍然進行渲染,仍然佔有空間,仍然能夠進行交互前端

  1. 使用Offstage
Offstage(
      offstage: true,
      child: child
      )
    );
複製代碼

隱藏後不會進行渲染,不佔用空間android

  1. 使用visibility
Visibility(
      visible: false,
      child: child
      )
    )
複製代碼

隱藏後不會進行渲染,不佔用空間ios

  • 實現gridview單擊一個item放大後左右滑動瀏覽

場景:

場景
閱讀原文下面是gridView展現的widget, 但願的效果是: 點擊第二個item放大後
但願的效果

方法

  1. 使用Swiper
Swiper(
          itemCount: w_list.length,
          //itemWidth: MediaQuery.of(context).size.width,
          itemBuilder: _swiperBuilder,
          pagination: SwiperPagination(
              alignment: Alignment.bottomCenter,
              margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
              builder: DotSwiperPaginationBuilder(
                  color: Colors.black54,
                  activeColor: Colors.blue,
              )
          ),
          controller: SwiperController(),
          autoplayDisableOnInteraction: true,
          scrollDirection: Axis.horizontal,
          autoplay: false,
          index: initial_index,//設置成開始瀏覽時點擊的item的index值
          viewportFraction: 1.0,
          scale: 1.0,
          //layout: SwiperLayout.STACK,
          //onTap: (index) => print('點擊了第$index'),
        ),
複製代碼

使用pageview能夠實現放大左右切換,可是相比swiper
(1)沒有自帶分頁器
(2)pageview的pageViewController.jumpToPage()方法在必須在PageView構建後纔可使用,可是咱們但願開始瀏覽時就已經跳轉到目標item,沒法知足需求。Swiper只須要簡單設置index即可以解決上述問題git

  • 場景同上,其中同時包含視頻和圖片類型的item,能夠作到滑動瀏覽及在線視頻點擊播放

方法

在Swiper的itemBuilder中,對於視頻類型的item,返回的widget並不是視頻,而是視頻的第一幀加一個播放圖標堆疊在上面,widget代碼以下github

Widget get_video_widget(String url)
{
  /*
  返回存放視頻的容器,參數是視頻第一幀url
   */
  return Container(
      decoration: BoxDecoration(
        image: new DecorationImage(
          // fit:BoxFit.fill,
            image: new NetworkImage(url, scale: 1.2)),
      ),
    child:Opacity(
          opacity: 0.8,
          child: Center(
              child:Icon(Icons.play_circle_filled,color: Colors.white,)
          ),
        ),
  );
}
複製代碼

當檢測到用戶點擊時,彈出dialog形式的新的界面進行視頻播放,而非Swiper內部播放,(Swiper內部播放會出現不少問題)json

IjkMediaController jk_controller = IjkMediaController();
return GestureDetector(
          child:w_list[index],//是上面 get_video_widget返回的widget
          onTap: (){
            showIJKDialog();
          },
        );
      }
      
@override
void dispose() {
    jk_controller?.dispose();
    super.dispose();
}

void showIJKDialog() async {
    await jk_controller.setDataSource(
      DataSource.network(
          "http://img.ksbbs.com/asset/Mon_1703/05cacb4e02f9d9e.mp4"),
    );
    await jk_controller.play();
    await showDialog(
      context: context,
      builder: (_) => _buildIJKPlayer(),
    );
    jk_controller.pause();
  }

_buildIJKPlayer() {
    return IjkPlayer(
      mediaController: jk_controller,
    );
  }
複製代碼

播放效果以下 bash

播放效果

這裏使用的用來播放視頻的flutter庫叫作flutter_ijkplayer,
相比較video_player,擁有更好的ui界面及更多的視頻操做功能,
相比較cheiwe,擁有更快的加載速度,更高的穩定性
一個很好的flutter_ijkplayer demo地址服務器

  • 實現http請求

方法

  1. 使用http請求
String url = "http://xxxxxxx"
    Map<String,String> json_data = {};
    json_data.addAll({"xx":xxx});
    await http.post(url, body: json_data)
        .then((response) {
      print("post方式->status: ${response.statusCode}");
      print("post方式->body: ${response.body}");
    });
複製代碼
  • 顯示圓形的container

方法

  1. 直接看代碼
ClipOval(
      child: Container(
        height: MediaQuery.of(context).size.height/4,
        width: MediaQuery.of(context).size.height/4,
        child: Image.file(File(widget.imagePath),fit: BoxFit.fill,),
      ),
    );
複製代碼
  • Camera自定義拍照

方法

  1. 看代碼 基本思路是 獲取可用相機 取第一個相機 獲得相機控制器 控制器初始化 打開相機預覽 獲得照片保存路徑 控制器拍照 展現拍照結果
import 'dart:async';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' show join;
import 'package:path_provider/path_provider.dart';

Future<void> main() async {
  // 獲取設備可用攝像頭
  final cameras = await availableCameras();

  // 取第一個攝像頭
  final firstCamera = cameras.first;

  runApp(
    MaterialApp(
      theme: ThemeData.dark(),
      home: TakePictureScreen(
        // Pass the appropriate camera to the TakePictureScreen widget.
        camera: firstCamera,
      ),
    ),
  );
}

// A screen that allows users to take a picture using a given //camera.
class TakePictureScreen extends StatefulWidget {
  final CameraDescription camera;

  const TakePictureScreen({
    Key key,
    @required this.camera,
  }) : super(key: key);

  @override
  TakePictureScreenState createState() => TakePictureScreenState();
}

class TakePictureScreenState extends State<TakePictureScreen> {
  CameraController _controller;
  Future<void> _initializeControllerFuture;

  @override
  void initState() {
    super.initState();
    // 獲取攝像頭的控制器
    _controller = CameraController(
      // Get a specific camera from the list of available cameras.
      widget.camera,
      ResolutionPreset.medium,
    );

    // 初始化控制器
    _initializeControllerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    //銷燬控制器
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Take a picture')),
      //FutureBuilder來確保控制器已經被初始化
      body: FutureBuilder<void>(
        future: _initializeControllerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            //肯定初始化完成,打開相機
            return CameraPreview(_controller);
          } else {
            // 不然,展現進度條.
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.camera_alt),
        onPressed: () async {
          try {
            // 確保相機已經被初始化
            await _initializeControllerFuture;

        
            final path = join(
              //獲得圖片保存路徑
              (await getTemporaryDirectory()).path,
              '${DateTime.now()}.png',
            );

            // 進行拍照
            await _controller.takePicture(path);

            // 拍照後進行展現
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => DisplayPictureScreen(imagePath: path),
              ),
            );
          } catch (e) {
            print(e);
          }
        },
      ),
    );
  }
}

//展現拍照結果的界面
class DisplayPictureScreen extends StatelessWidget {
  final String imagePath;

  const DisplayPictureScreen({Key key, this.imagePath}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Display the Picture')),
      //根據路徑獲得圖片
      body: Image.file(File(imagePath)),
    );
  }
}
複製代碼
  • ui實現懸浮設計

方法

  1. 使用Container的decoration設置背景,
    內嵌---Container,經過decoration的gradient設置背景圖上層的一個漸變色渲染
    內嵌---Stack,進行佈局cookie

  2. 一些color使用Color.fromRGBO(247, 64, 106, 0.5),經過調節透明度實現一個懸浮效果網絡

new Container(
              decoration: new BoxDecoration(
                image: backgroundImage,
              ),
              child: new Container(
                  decoration: new BoxDecoration(
                      gradient: new LinearGradient(
                    colors: <Color>[
                      const Color.fromRGBO(162, 146, 199, 0.8),
                      const Color.fromRGBO(51, 51, 63, 0.9),
                    ],
                    stops: [0.2, 1.0],
                    begin: const FractionalOffset(0.0, 0.0),
                    end: const FractionalOffset(0.0, 1.0),
                  )),
                  child: new ListView(
                    padding: const EdgeInsets.all(0.0),
                    children: <Widget>[
                      new Stack(
                        alignment: AlignmentDirectional.bottomCenter,
                        children: <Widget>[
                          new Column(
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                            children: <Widget>[
                              Container(height: 10,
                              child:Text("懸浮設計")),
                            ],
                          ),
                          
                        ],
                      ),
                    ],
                  ))),
複製代碼
  • ui保證異步操做結束後進行build

方法

  1. 使用FutureBuilder

注意:

  • initState和構造函數不能夠直接間接調用異步方法
  • FutureBuilder 必須提供一個異步結束前顯示的widget,通常是
Center(child: CircularProgressIndicator());
複製代碼

代碼示例:

FutureBuilder<void>(
        future: _initializeControllerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            // If the Future is complete, display the preview.
            return CameraPreview(_controller);
          } else {
            // Otherwise, display a loading indicator.
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
複製代碼
  • 前端使用preferences

方法

  1. 添加依賴到pubspec.yaml
shared_preferences: ^0.4.3
複製代碼
  1. 存數據
final prefs = await SharedPreferences.getInstance();

prefs.setString("name","Jerry");
複製代碼

3.取數據

final prefs = await SharedPreferences.getInstance();

final  name = prefs.getString("count") ?? "";
複製代碼
  • build結束後的監聽事件

方法

  1. 使用WidgetsBinding WidgetBinding類官方解釋是控制層和Flutter引擎之間的粘合劑,這個類能夠監聽第一幀的繪製結束,第一幀繪製結束標誌着build完成,使用方法見代碼
WidgetsBinding widgetsBinding;//用來監聽build結束後的響應事件
@override
  void initState() {
    super.initState();
    widgetsBinding = WidgetsBinding.instance;
    widgetsBinding.addPostFrameCallback((callBack){
      dealWithAutoLand();
      dealWithRememberPw();
    });
  }
複製代碼
  • 使用Form表單構建輸入框

方法:

使用兩個文件
InputFields.dart構建單獨一個輸入框widget,代碼

import 'package:flutter/material.dart';


//對應一個輸入框
class InputFieldArea extends StatelessWidget {
  final String hint;
  final bool obscure;
  final IconData icon;
  TextEditingController controller;
  FormFieldValidator validator;
  InputFieldArea({this.hint, this.obscure, this.icon,this.controller,this.validator});
  @override
  Widget build(BuildContext context) {
    return (new Container(
      decoration: new BoxDecoration(
        border: new Border(
          bottom: new BorderSide(
            width: 0.5,
            color: Colors.white24,
          ),
        ),
      ),
      child: new TextFormField(
        controller: controller,
        autofocus: true,
        obscureText: obscure,
        validator: validator,
        style: const TextStyle(
          color: Colors.white,
        ),
        decoration: new InputDecoration(
          icon: new Icon(
            icon,
            color: Colors.white,
          ),
          border: InputBorder.none,
          hintText: hint,
          hintStyle: const TextStyle(color: Colors.white, fontSize: 15.0),
          contentPadding: const EdgeInsets.only(
              top: 30.0, right: 30.0, bottom: 30.0, left: 5.0),
        ),
      ),
    ));
  }
}
複製代碼

form.dart,組織全部輸入框,代碼

TextEditingController studentNumberControllerLogin = new TextEditingController();
TextEditingController passwordNumberControllerLogin = new TextEditingController();

//登錄的輸入表單,包含兩個輸入框
class loginFormContainer extends StatelessWidget {

  Map<String,String> inputData;

  //獲得輸入數據函數
  void getData()
  {
    inputData = new Map();
    inputData["studentNumber"] = studentNumberControllerLogin.text.toString();
    inputData["password"] = passwordNumberControllerLogin.text.toString();
  }

  @override
  Widget build(BuildContext context) {
    return (new Container(
      margin: new EdgeInsets.symmetric(horizontal: 20.0),
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          new Form(
              child: new Column(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: <Widget>[
                  new InputFieldArea(
                    controller: studentNumberControllerLogin,
                    hint: "學號",
                    obscure: false,
                    icon: Icons.person_outline,
                  ),
                  new InputFieldArea(
                    controller: passwordNumberControllerLogin,
                    hint: "密碼",
                    obscure: true,
                    icon: Icons.lock_outline,
                  ),
                ],
              )),
        ],
      ),
    ));
  }
}
複製代碼
  • 設置appBar高度

方法

  1. 使用PreferredSize
appBar:PreferredSize(
        preferredSize: Size.fromHeight(2.0),//修改appbar高度
        child:  AppBar(
          title: Text("地大知識交流平臺"),
        ),
      ),
複製代碼
  • 設置漸變色

方法

1.gradient使用LinearGradinet,代碼

Container(
             decoration: BoxDecoration(
               gradient: LinearGradient(
                 colors: [Colors.cyan, Colors.blue, Colors.cyan],
               ),
),
複製代碼
  • Container各個屬性使用

給個不錯的連接

blog.csdn.net/chenlove1/a…

  • 屏幕適配

方法

  1. 安裝,寫入pubspec.yaml處
flutter_screenutil: ^0.5.3
複製代碼
  1. materialApp的入口處,即home頁面,傳入設計手稿設計尺寸,進行初始化。整個app使用過程當中只須要初始化一次
//填入設計稿中設備的屏幕尺寸
ScreenUtil.instance = ScreenUtil(width: 1080, height: 1920)..init(context);
複製代碼
  1. 使用時,分紅字體大小、正方形、寬度、高度設置
  • 字體
ScreenUtil.getInstance().setSp(28) 
複製代碼
  • 正方形
Container(
           width: ScreenUtil.getInstance().setWidth(300),
           height: ScreenUtil.getInstance().setWidth(300),
            ),
複製代碼
  • 寬度
ScreenUtil.getInstance().setWidth(300),
複製代碼
  • 高度
ScreenUtil.getInstance().setHeight(300),
複製代碼
  • 上傳圖片到服務器

方法:使用dio

第一步,安裝dio

dio: ^2.1.0
複製代碼

第二步,封裝dio

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

//普通格式的header
Map<String, dynamic> headers = {
  "Accept":"application/json",
//  "Content-Type":"application/x-www-form-urlencoded",
};

//json格式的header
Map<String, dynamic> headersJson = {
  "Accept":"application/json",
  "Content-Type":"application/json; charset=UTF-8",
};

class HttpUtil {
  Dio dio;
  Map<String, dynamic> headers;
  BaseOptions options;

  HttpUtil({String baseUrl}) {
    headers = {
      "Accept":"application/json",
//  "Content-Type":"application/x-www-form-urlencoded",
    };

    print('dio賦值');
    // 或者經過傳遞一個 `options`來建立dio實例
    options = BaseOptions(
      // 請求基地址,通常爲域名,能夠包含路徑
      baseUrl: baseUrl,
      //鏈接服務器超時時間,單位是毫秒.
      connectTimeout: 10000,
      //[若是返回數據是json(content-type),dio默認會自動將數據轉爲json,無需再手動轉](https://github.com/flutterchina/dio/issues/30)
      responseType:ResponseType.plain,
      ///  響應流上先後兩次接受到數據的間隔,單位爲毫秒。若是兩次間隔超過[receiveTimeout],
      ///  [Dio] 將會拋出一個[DioErrorType.RECEIVE_TIMEOUT]的異常.
      ///  注意: 這並非接收數據的總時限.
      receiveTimeout: 3000,
      headers: headers,
    );
    dio = new Dio(options);
    dio.interceptors.add(CookieManager(CookieJar()));
  }

  get(url, {data, options, cancelToken}) async {
    print('get請求啓動! url:$url ,body: $data');
    Response response;
    try {
      response = await dio.get(
        url,
        cancelToken: cancelToken,
      );
      print('get請求成功!response.data:${response.data}');
    } on DioError catch (e) {
      if (CancelToken.isCancel(e)) {
        print('get請求取消! ' + e.message);
      }
      print('get請求發生錯誤:$e');
    }
    return response.data;
  }

  post(url, {data, options, cancelToken}) async {
    print('post請求啓動! url:$url ,body: $data');
    Response response;
    try {
      response = await dio.post(
        url,
        data: data,
      );
      print('post請求成功!response.data:${response.data}');
    } on DioError catch (e) {
      if (CancelToken.isCancel(e)) {
        print('post請求取消! ' + e.message);
      }
      print('post請求發生錯誤:$e');
    }
    return response.data;
  }
}
複製代碼

第三步,上傳圖片

String url = "http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    FormData formData = new FormData.from({
      "image01": new UploadFileInfo(file, "head.jpg")
    });
    HttpUtil httpUtil = new HttpUtil(baseUrl: url);
    String response = await httpUtil.post(url,data: formData);
    print(response);
    
複製代碼
  • flutter使用toast

方法

第一步,安裝toast插件

toast: ^0.1.3
複製代碼

第二步,使用

//顯示toast
  void showToastContent(String content)
  {
    Toast.show(content,curContext,duration: Toast.LENGTH_LONG,gravity: Toast.BOTTOM);
  }
複製代碼
  • flutter網絡請求判斷出網絡緣由

方法

設置異常處理塊,思想是捕獲到異常則表明網絡鏈接失敗

List<String> splits = response.split("_");
    String resultMark = splits[0];
    String content;
    try
    {
      content = splits[1];
    }catch(e,s)
    {
      content = "網絡緣由,登錄失敗";
    }
複製代碼
  • 返回攔截,防止用戶意外退出軟件

方法

DateTime lastTime;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(title: Text("點兩次返回鍵退出"),),
            body: Builder(builder: (context)=>WillPopScope(
                onWillPop: () async {
                  if (lastTime == null || DateTime.now().difference(lastTime) >
                      Duration(seconds: 1)) {
                    lastTime = DateTime.now();
                    Scaffold.of(context).showSnackBar(SnackBar(content: Text("再點一次退出!")));
                    return false;
                  }
                  return true;
                },
                child: Container(
                  alignment: Alignment.center,
                  child: Text("1秒內連續按兩次返回鍵退出"),
                )
            ))
        )
    );
  }
複製代碼
  • 強制豎屏不可旋轉屏幕

方法

import 'package:flutter/services.dart';
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((_){});
複製代碼
  • 禁止debug信息顯示在手機屏幕上

方法

main函數內部放

ErrorWidget.builder = (FlutterErrorDetails details) => Container();
複製代碼
  • 定時前臺顯示通知

方法

1 安裝插件

flutter_local_notifications: ^0.8.4
複製代碼

2 配置android
2.1 AndroidManifest.xml權限添加

<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
複製代碼

2.2 drawable下建立圖標名爲app_icon

3 配置ios 這一步不須要作,只要一步一步來,後面的代碼中會包含

4 相關代碼

FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;

//初始化
 var initializationSettingsAndroid =
    AndroidInitializationSettings('app_icon');
    var initializationSettingsIOS = IOSInitializationSettings(
        onDidReceiveLocalNotification: onDidReceiveLocalNotification);
    var initializationSettings = InitializationSettings(
        initializationSettingsAndroid, initializationSettingsIOS);
    flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
    flutterLocalNotificationsPlugin.initialize(initializationSettings,
        onSelectNotification: onSelectNotification);
        


  //顯示通知的函數
  Future<void> showNotification(String content) async {
    
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
        'your channel id', 'your channel name', 'your channel description',
        importance: Importance.Max, priority: Priority.High, ticker: 'ticker');
    var iOSPlatformChannelSpecifics = IOSNotificationDetails();
    var platformChannelSpecifics = NotificationDetails(
        androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
    int notiId = prefs.getInt("notiId") ?? 0;
    prefs.setInt("notiId",notiId + 1);

    await flutterLocalNotificationsPlugin.show(
        notiId, '地大支持平臺', "有人解答了你的問題", platformChannelSpecifics,
        payload: content);
  }
  
  //通知點擊事件
  Future<void> onSelectNotification(String payload) async {
    if (payload != null) {
      print("payload*****************************" + payload);
    }
    
    await Navigator.push(
      projectTopContext,
      MaterialPageRoute(builder: (context) => showAnswerComplete(xxx)),
    );
  }
  
  //適配ios老版本,與點擊事件響應一致
  Future onDidReceiveLocalNotification(
      int id, String title, String body, String payload) async {
    // display a dialog with the notification details, tap ok to go to another page
    showDialog(
      context: context,
      builder: (BuildContext context) => new CupertinoAlertDialog(
            title: new Text(title),
            content: new Text(body),
            actions: [
              CupertinoDialogAction(
                isDefaultAction: true,
                child: new Text('Ok'),
                onPressed: () async {
                  Navigator.of(context, rootNavigator: true).pop();
                  Navigator.push(
                  projectTopContext,
                  MaterialPageRoute(builder: (context) => showAnswerComplete(xxx))
                  );
                },
              )
            ],
          ),
    );
  }
複製代碼
相關文章
相關標籤/搜索