Flutter踩坑日記(持續更新...)

歡樂症患者發表於2020-05-20

標題後面的時間是我用來記錄自己更新上去的時間,不用在意

設定元件為中文(2020.05.20)

剛開始去使用一些彈窗元件(例如時間選擇器什麼的),發現裡面的確定和取消都是英文,就去百度了一下方法。

改配置檔案

在pubspec.yaml檔案的對應位置加上

  flutter_localizations:
    sdk: flutter
複製程式碼

Flutter踩坑日記(持續更新...)

修改主入口

在主入口函式新增(這裡還需要引入package:flutter_localizations/flutter_localizations.dart)

import 'package:flutter_localizations/flutter_localizations.dart'; 
複製程式碼
      //設定元件為中文
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        //此處
        const Locale('zh', 'CH'),
        const Locale('en', 'US'),
      ],
      locale: Locale('zh'),
複製程式碼

Flutter踩坑日記(持續更新...)

圖片上傳(2020.05.20)

這個坑,花了我整整一天的時間(其實就是電腦卡,10分鐘一小卡,30分鐘一大卡,不好除錯)。

Flutter踩坑日記(持續更新...)

外掛

image_picker

開始

直接貼程式碼

import 'dart:collection';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:image_picker/image_picker.dart';
import 'package:flutter/material.dart';
import '../../base/Http.dart';//這個是自己封裝後的http
import 'package:http_parser/http_parser.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  File _image;

    //照相
  Future getImage() async {
    var image = await ImagePicker.pickImage(source: ImageSource.camera);
    setState(() {
      uploadImgFunc(image);
      _image = image;
    });
  }
    //從相機選擇
  Future openGallery() async {
    var image = await ImagePicker.pickImage(source: ImageSource.gallery);
    setState(() {
      uploadImgFunc(image);
      _image = image;
    });
  }

  uploadImgFunc(File image) async {
    String path = image.path;//檔案路徑
    String name = path.substring(path.lastIndexOf("/") + 1, path.length);//檔名
    String suffix = name.substring(name.lastIndexOf(".") + 1, name.length);//檔案字尾
    FormData formData = FormData.fromMap({
      "uploadFile":await MultipartFile.fromFile(
        path,
        filename: name,
        contentType:MediaType('image',suffix)//contentType這個引數看情況,下面會講到
      ),
    });
    Dio dio = new Dio();
    var result = await dio.post<String>("介面", data: formData);
    print(result);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Picker Example'),
      ),
      body: Center(
        child: _image == null ? Text('No image selected.') : Image.file(_image),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: getImage,
        tooltip: 'Pick Image',
        child: Icon(Icons.add_a_photo),
      ),
    );
  }
}

複製程式碼

上面就是一個例子了,現在開始講解。

首先,我們需要引入image_picker這個包,在pubspec.yaml檔案裡面新增對應項

Flutter踩坑日記(持續更新...)
然後在對應檔案引入

import 'dart:io';//io操作
import 'package:dio/dio.dart';
import 'package:image_picker/image_picker.dart';
複製程式碼

寫呼叫方法(這裡以拍照為例)

  Future getImage() async {
    var image = await ImagePicker.pickImage(source: ImageSource.camera);//這裡用到了對應外掛
    setState(() {
      uploadImgFunc(image);
      _image = image;//_image需要在外層定義
    });
  }
複製程式碼

寫上傳方法(重點)

  uploadImgFunc(File image) async {
    String path = image.path;
    String name = path.substring(path.lastIndexOf("/") + 1, path.length);
    String suffix = name.substring(name.lastIndexOf(".") + 1, name.length);
    FormData formData = FormData.fromMap({
      "uploadFile":await MultipartFile.fromFile(//這裡記得加await
        path,
        filename: name,
        contentType:MediaType('image',suffix)
      ),
    });
    Dio dio = new Dio();
    var result = await dio.post<String>('介面', data: formData);
    print(result);
  }
複製程式碼

dio3.0之後就使用MultipartFile不再使用UploadFileInfo。

MediaType這個需要額外引一個標頭檔案,不然會報錯

import 'package:http_parser/http_parser.dart';
複製程式碼

假如,你們的後端是可以用二進位制的方法來上傳的話,

contentType:MediaType('image',suffix)
複製程式碼

上面那一行可以去掉,不然的話一定要加上,不然後端如果是判斷檔案型別的話可能就過不了。我就是因為這個卡了很久......

ios真機除錯的時候用相機(我同事說的)好像會閃退,因為我沒有mac系統,也沒錢,這裡找個了文章。大家看看吧。

官網文件好像也有說,之前沒看仔細。需要在配置檔案裡面加東西

Flutter踩坑日記(持續更新...)
Flutter踩坑日記(持續更新...)

Flutter踩坑日記(持續更新...)

獲取元素位置(2020.05.20)

現在,我要獲取某個元素的位置,然後按照位置來定義一個彈窗(showMenu)的位置,例如下圖

Flutter踩坑日記(持續更新...)

定義一個key

GlobalKey positionKey = GlobalKey();
複製程式碼

在需要定位的元素上使用

Text(
      '其他',
      key: positionKey,
      textAlign: TextAlign.center,
      style: TextStyle(
        height: 2.5,
        color: Color.fromRGBO(255, 106, 0, 1),
        fontSize: ScreenAdaper.size(30),
   )
複製程式碼

使用key來獲取位置

在函式內去獲取

    RenderBox renderBox =
        positionKey.currentContext.findRenderObject();
    var offset = renderBox.localToGlobal(Offset.zero);
    print(offset.dx)//橫座標
    print(offset.dx)//縱座標
複製程式碼

ios數字軟鍵盤沒有'完成'按鈕(2020.05.20)

當我們把輸入框(TextField)的型別(keyboardType)設定number時,Android和ios就會彈出數字鍵盤而不是英文鍵盤,但是ios數字軟鍵盤是沒有所謂的完成按鈕(就是點選後軟鍵盤會收回去)。這時候,我們就需要用到一個外掛了

keyboard_actions

這是一個可以解決上面問題的外掛,如何引入同上面提過的。

使用

在需要用到的元件內先定義一個基礎設定,這裡的actions裡面KeyboardAction有很多設定,推薦去看一下官方文件

  KeyboardActionsConfig _buildConfig(BuildContext context) {
    return KeyboardActionsConfig(
      keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
      keyboardBarColor: Colors.grey[200],
      nextFocus: true,
      actions: [
        KeyboardAction(
          focusNode: _nodeNumberNode,//下面會提到
          toolbarButtons: [
            (node) {
              return GestureDetector(
                onTap: () => node.unfocus(),
                child: Container(
                  padding: EdgeInsets.fromLTRB(8, 8, 16, 8),
                  child: Text(
                    "完成",
                    style: TextStyle(color: Colors.black),
                  ),
                ),
              );
            },
          ],
        ),
      ],
    );
  }
複製程式碼

然後,我們需要定義一個FocusNode來對應設定和輸入框

final FocusNode _nodeNumberNode = FocusNode();
複製程式碼

輸入框設定,需要包一層KeyboardActions

KeyboardActions(
    config: _buildConfig(context),//上面定義的那個基礎設定
    chlid:Container(
        child:TextField(
            focusNode: _nodeNumberNode,
            keyboardType: TextInputType.number,
            textInputAction: TextInputAction.done,
            decoration: InputDecoration(
                hintText: '請輸入',
                border: InputBorder.none,
            ),
            autofocus: false,
            onChanged: (value) {
                setState(() {
                    //賦值操作
                });
            },
        )
    )
)
複製程式碼

Flutter踩坑日記(持續更新...)
這樣就可以了,不過有個要注意的點,然後你的輸入框是放在ListView裡面的話,需要給ListView加一個屬性

shrinkWrap: false,
複製程式碼

showDialog檢視資料重新整理不及時(2020.05.21)

使用showDialog後,通過setState()無法更新當前dialog。因為dialog其實是另一個頁面,準確地來說是另一個路由,因為dialog的關閉也是通過navigator來pop的,所以它的地位跟你當前主頁面一樣,所以我們需要使用另外的方式來實現。

我們先看看showDialog的最基本寫法

showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        ...
      );
    }
);
複製程式碼

第一種方法

解決問題後的寫法

showDialog(
    context: context,
    builder: (context) {
      return StatefulBuilder(//這裡需要先使用StatefulBuilder
        builder: (context, state) {//多了個state
          return AlertDialog(
            title: Text('彈窗標題'),
            content: Container(
              height: ScreenAdaper.height(200),//ScreenAdaper這個是自己的螢幕適配,大家可以直接使用數字的
              child: Column(
                children: <Widget>[
                  InkWell(
                    child: Row(
                      children: <Widget>[
                       Image.asset(
                            'images/task_leadicon.png',
                            width:ScreenAdaper.height(60),
                        )
                      ],
                    ),
                    onTap: () async {
                      state(() {//這個是重點
                        //...這裡進行賦值操作
                      });
                    },
                  ),
                ],
              ),
            ),
            actions: <Widget>[
              FlatButton(
                child: Text('確認'),
                onPressed: () async {
                  Navigator.pop(context);
                },
              ),
              FlatButton(
                child: Text('取消'),
                onPressed: () {
                  Navigator.pop(context);
                },
              ),
            ],
          );
        },
      );
    });
複製程式碼

第二種方法

這個還有另外一種寫法,就是自己再去定義一個彈窗,不過這種我沒有試過,這裡直接貼別人的程式碼

showDialog(
    context: context,
    builder: (context) {
        String label = 'test';
        return DialogContent(
            label: label,
        );
    });
class DialogContent extends StatefulWidget {
  final String label;
  DialogContent({Key key, this.label}) : super(key: key);
  @override
  State<StatefulWidget> createState() => DialogContentState();
}
class DialogContentState extends State<DialogContent> {
  String label = '';
  @override
  void initState() {
    super.initState();
    label = widget.label;
  }
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Text(label),
      onTap: () {
        setState(() {
          label = 'test9';
        });
      },
    );
  }
}
複製程式碼

Flutter踩坑日記(持續更新...)
上面的坑都是我自己在開發遇到並且解決了的,如果你也遇到這些坑,而且我提的方法也有用的話,給個贊吧!!!如果沒有起到作用,能否在評論區留下有用的方法

相關文章