1. 程式人生 > 實用技巧 >flutter 自定義彈框

flutter 自定義彈框

在Flutter應用開發中,經常會遇到自定義彈框的開發需求,如下圖所示。


對於這種樣式,我們可以選擇自定義Dialog,具體的樣式可以根據自己的需要進行修改。 例如,下面是我的實現,由於文字是一個列表,所以我需要新建一個實體類,如下所示。

class IntroduceModel {

  int code;
  List<Data> data;

  IntroduceModel({this.code, this.data});

  IntroduceModel.fromjson(Map<String, dynamic> json) {
    code = json['code'];
    if (json['data'] != null) {
      data = new List<Data>();
      json['data'].forEach((v) {
        data.add(new Data.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['code'] = this.code;
    if (this.data != null) {
      data['data'] = this.data.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Data {
  String itemTitle;
  String itemContent;

  Data({this.itemTitle, this.itemContent});

  Data.fromJson(Map<String, dynamic> json) {
    itemTitle = json['itemTitle'];
    itemContent = json['itemContent'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['itemTitle'] = this.itemTitle;
    data['itemContent'] = this.itemContent;
    return data;
  }
}

然後,我們自定義一個Dialog,原始碼如下:

class IntroduceDialog extends Dialog {
  
  String des='注:以上的單⽇⽬標額從實際年⽬標額、⽉⽬標額取平均值計算⽽來。';
  String title;
  String content;
  bool isForce;
  IntroduceModel model;
  IntroduceDialog({this.title, this.content,this.isForce}){
    if(content!=null){
      model=IntroduceModel.fromJson(json.decode(content));
    }
  }


  @override
   build(BuildContext context) {
    return Material(
      type: MaterialType.transparency,
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _buildContent(context),
            _buildClose(context)
          ],
        ),
      ),
    );
  }

  _buildContent(BuildContext context) {
    double sWidth= MediaQuery.of(context).size.width;
    return Container(
      color: Colors.white,
      width: sWidth*0.85,
      height: 420,
      padding: EdgeInsets.only(left: 15,right: 15,top: 10,bottom: 10),
      child: SingleChildScrollView(
        child: Column(
          children: [
            Text(title, style: TextStyle(fontSize: 20,color:Colors.black,fontWeight: FontWeight.bold)),
            SizedBox(height: 5),
            _buildListView(),
            Text(des, style: TextStyle(fontSize: 14)),
          ],
        ),
      ),
    );
  }

  _buildListView() {
    return Container(
      padding: EdgeInsets.only(top: 5,bottom:5,right: 0,),
      child: ListView.separated(
        shrinkWrap: true,
        physics:  NeverScrollableScrollPhysics(),
        itemCount: model.data.length,
        separatorBuilder: (BuildContext context, int index) => Container(
          child:  divider(),
        ),
        itemBuilder: (context, index) {
          return _buildItem(context,model.data, index);
        },
      ),
    );
  }

  _buildItem(BuildContext context, list, int index) {
    Data item=list[index];
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(item.itemTitle, style: TextStyle(fontSize: 14)),
        Text(item.itemContent, style: TextStyle(fontSize: 14)),
      ],
    );
  }

  _buildClose(BuildContext context) {
    return GestureDetector(
      child: Offstage(
        offstage: isForce,
        child: Container(
            margin: EdgeInsets.only(top: 30),
            child: LoadAssetImage('close_icon', width: 45,height: 45,fit: BoxFit.fill,)
        ),
      ),
      onTap: (){
        Navigator.of(context).pop();
      },
    );
  }


  static showUpdateDialog(BuildContext context, String title,String content, bool isForce) {
    return showDialog(
        barrierDismissible: false,
        context: context,
        builder: (BuildContext context) {
          return WillPopScope(
              child: IntroduceDialog(title: title,content: content, isForce: isForce),onWillPop: _onWillPop);
        });
  }

  static Future<bool> _onWillPop() async{
    return false;
  }


}

需要說的是,自定義的Dialog的根元件需要使用Material,不然的話,Dialog介面的文字下面會出現黃色的下劃線,導致這種情況發生的原因是因為,Text widget 隸屬於Material 風格下的元件,如果根節點不是Material 相關元件,則會使用預設帶黃色下劃線的格式。如果根節點是Material 容器元件,則會採用其Material風格的樣式(即不帶有下換線),解決的方法有三個:

廣州vi設計http://www.maiqicn.com 辦公資源網站大全https://www.wode007.com

解決方案主要有三種:

1,採用根節點為腳手架Scaffold元件:

Scaffold(body: content,);

2, 採用根節點為Material 元件。

Material(child: content);

3, 逐個修改Text 元件的style 下的decoration為TextDecoration.none。

child: Text(
    "專欄的文章",
     overflow: TextOverflow.ellipsis,
     style: TextStyle(
             decoration: TextDecoration.none,
             color: Color(0xFF888888),
             fontSize: 14,
             fontWeight: FontWeight.bold,
             fontFamily: defaultFontFamily,
      ),
 )

對於間距,只需要使用Offstage元件即可。最後,在需要使用的地方呼叫一下這個元件即可,如下所示。

 showIntroduceDialog(BuildContext context) async {
    var localData=await rootBundle.loadString('assets/flow_advertis_funnel.json');
    IntroduceDialog.showUpdateDialog(context, '資料說明', localData, false);
  }