超过百万的StackOverflow Flutter 问题-第二期

老孟导读:一个月前分享的《超过百万的StackOverflow Flutter 问题-第一期》受到很多朋友的喜欢,非常感谢大家的支持,在文章末尾有第一期的链接,希望此文能对你有所帮助。

No connected devices

这个问题估计大部分都遇到过,解决方法如下:

  1. 执行flutter doctor

    Doctor summary (to see all details, run flutter doctor -v):
    [✓] Flutter (Channel stable, v1.12.13+hotfix.9, on Mac OS X 10.14.6 18G1012,
        locale zh-Hans-CN)
     
    [!] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
        ! Some Android licenses not accepted.  To resolve this, run: flutter doctor
          --android-licenses
    [✓] Xcode - develop for iOS and macOS (Xcode 11.3.1)
    [✓] Android Studio (version 3.5)
    [✓] Connected device (1 available)
    
    ! Doctor found issues in 1 category.
    

    保证没有红色的叉。

  2. 启动手机或者模拟器(Android系统大于16),开启USB 调试模式,不同手机开启方法略有不同,以华为手机为例:进入设置->系统->关于手机,快速连续点击版本号5次,提示打开开发者模式,返回设置,此时会出现开发人员选项菜单,进入,打开开发人员选项USB 调试,弹出授权菜单,同意即可。

  3. 打开Android Studio,查看连接的手机:

    enter image description here
  4. 如果依然无法连接手机,打开Android Studio设置界面:

    enter image description here

    选择最近的API。

  5. 到此基本就可以解决了,如果还无法连接,那基本就是adb的问题,很可能是adb端口被占用,关于adb的解决方案可百度,引起adb问题有很多种情况。

创建Toast提示

Material Design设计规范中Snackbars就是Toast提示,Snackbar用法如下:

Scaffold.of(context).showSnackBar(SnackBar(
      content: Text("Sending Message"),
    ));
enter image description here

这个效果在国内来不是很接受,所以一般使用第三方插件fluttertoast

Fluttertoast.showToast(
        msg: "This is Toast messaget",
        toastLength: Toast.LENGTH_SHORT,
        gravity: ToastGravity.CENTER,
        timeInSecForIos: 1
    );
enter image description here

创建一个圆角Button

创建圆角Button的方式有很多种,下面介绍几种简单的:

  1. 使用 FlatButtonRaisedButton

    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(18.0),
      side: BorderSide(color: Colors.red)
    ),
    
    enter image description here
  2. 使用ClipRRect

    ClipRRect(
      borderRadius: BorderRadius.circular(40),
      child: RaisedButton(
        onPressed: () {},
        child: Text("Button"),
      ),
    )
    
  3. 使用ButtonTheme

    ButtonTheme(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
      child: RaisedButton(
        onPressed: () {},
        child: Text("Button"),
      ),
    )
    

添加启动页

Flutter应用程序启动时会出现一段时间的白屏,因为程序要启动引擎,所以App第一次启动比较慢,在原生端会显示一段时间的白色启动页,我们把这个白色启动页做为应用程序的启动页,替换为自己的图片,此方案的启动页只能是一张图片,无法交互,如果需要启动页有交互效果建议使用Flutter做。

Android端替换启动页图片,打开android/app/src/main/res/drawable/launch_background.xml文件,效果如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/white" />

    <!-- You can insert your own image assets here -->
    <!-- <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/launch_image" />
    </item> -->
</layer-list>

修改为:

<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/splash" />
    </item>
</layer-list>

splash.png图片拷贝到drawable文件夹下。

iOS端,打开ios/Runner/Assets.xcassets/LaunchImage.imageset下面的3张LaunchImage.png图片替换,保持名称不变。

修改应用程序的包名/BundleIdentifier

Android平台上打开android/app/build.gradle:

defaultConfig {
    applicationId "com.example.fluttersample"
    minSdkVersion 16
    targetSdkVersion 28
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

修改applicationId属性即可。

iOS平台打开ios/Runner/Info.plist,修改CFBundleIdentifier的值:

<key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>

如何给一个控件添加边框

new Container(
  margin: const EdgeInsets.all(15.0),
  padding: const EdgeInsets.all(3.0),
  decoration: BoxDecoration(
    border: Border.all(color: Colors.blueAccent)
  ),
  child: Text("My Awesome Border"),
)
enter image description here

让Button充满父组件

SizedBox.expand(
  child: RaisedButton(...),
)

或者

SizedBox(
  width: double.infinity,
  // height: double.infinity,
  child: RaisedButton(...),
)

或者

ConstrainedBox(
    constraints: const BoxConstraints(minWidth: double.infinity),
    child: RaisedButton(...),
)

或者

ButtonTheme(
  minWidth: double.infinity,
  child: MaterialButton(
    onPressed: () {},
    child: Text('Raised Button'),
  ),
),

如何在Column中添加ListView

给ListView指定高度:

Column(
  children: <Widget>[
    Container(
      height: 50,
      child: ListView(),
    )
  ],
)

或者铺满Column:

Column(
  children: <Widget>[
     Expanded(
      child: horizontalList,
    )
  ],
);

如何给图片添加圆角

ClipRRect(
    borderRadius: BorderRadius.circular(8.0),
    child: Image.network(
        '',
    ),
)

或者

CircleAvatar(
  radius: 20,
  backgroundImage: NetworkImage('https://via.placeholder.com/140x100')
)

或者

ClipOval(
  child: Image.network(
    "image_url",
    height: 100,
    width: 100,
    fit: BoxFit.cover,
  ),
),

或者

Container(
        width: 100.0,
        height: 150.0,
        decoration: BoxDecoration(
          image: DecorationImage(
                fit: BoxFit.cover,
                image: NetworkImage('Path to your image')
              ),
          borderRadius: BorderRadius.all(Radius.circular(8.0)),
          color: Colors.redAccent,
        ),

如何去掉TextField的下划线

InputDecoration(
    border: InputBorder.none,
    hintText: 'Username',
  ),
),

如果防止UI随着手机的旋转而在横竖屏间切换

设置支持的方向:

class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
        DeviceOrientation.portraitDown,
      ]);
      return new MaterialApp(...);
    }
  }

打开ios/Runner/Info.plist,设置支持的方向:

<array>
    <string>UIInterfaceOrientationPortrait</string>
</array>

显示/隐藏控件

使用Opacity

Opacity(
  opacity: .0,
  child: ,
)

或者

Visibility(
  visible: false,
  child: ,
)

或者

Offstage(
  offstage: true,
  child: ,
)

如何截取Android的返回按键并处理

使用WillPopScope

@override
Widget build(BuildContext context) {
  return new WillPopScope(
    onWillPop: () async => false,
    child: new Scaffold(
      appBar: new AppBar(
        title: new Text("data"),
        leading: new IconButton(
          icon: new Icon(Icons.ac_unit),
          onPressed: () => Navigator.of(context).pop(),
        ),
      ),
    ),
  );
}

如何设置RaisedButton控件的width

ButtonTheme(
  minWidth: 200.0,
  height: 100.0,
  child: RaisedButton(
    onPressed: () {},
    child: Text("test"),
  ),
);

或者

SizedBox(
  width: 100, // specific value
  child: RaisedButton(...)
)

设置AppBar的height

使用PreferredSize

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Example',
      home: Scaffold(
        appBar: PreferredSize(
          preferredSize: Size.fromHeight(50.0), // here the desired height
          child: AppBar(
            // ...
          )
        ),
        body: // ...
      )
    );
  }
}

如何格式化时间

Dart API本身没有格式化时间的接口,使用intl

import 'package:intl/intl.dart';

DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd – kk:mm').format(now);

通过List绘制一组控件

Widget getTextWidgets(List<String> strings)
  {
    List<Widget> list = new List<Widget>();
    for(var i = 0; i < strings.length; i++){
        list.add(new Text(strings[i]));
    }
    return new Row(children: list);
  }

或者

Row(children: strings.map((item) => new Text(item)).toList())

或者

var list = ["one", "two", "three", "four"]; 

child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
             for(var item in list ) Text(item)
          ],
        ),    

如何设置GridView中组件的height

使用childAspectRatio,设置如下:

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> widgetList = ['A', 'B', 'C'];

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;

    /*24 is for notification bar on Android*/
    final double itemHeight = (size.height - kToolbarHeight - 24) / 2;
    final double itemWidth = size.width / 2;

    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Container(
        child: new GridView.count(
          crossAxisCount: 2,
          childAspectRatio: (itemWidth / itemHeight),
          controller: new ScrollController(keepScrollOffset: false),
          shrinkWrap: true,
          scrollDirection: Axis.vertical,
          children: widgetList.map((String value) {
            return new Container(
              color: Colors.green,
              margin: new EdgeInsets.all(1.0),
              child: new Center(
                child: new Text(
                  value,
                  style: new TextStyle(
                    fontSize: 50.0,
                    color: Colors.white,
                  ),
                ),
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

如何修改状态条颜色

使用flutter_statusbarcolor

import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    FlutterStatusbarcolor.setStatusBarColor(Colors.white);
    return MaterialApp(
      title: app_title,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(title: home_title),
    );
  }
}

或者

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
  statusBarColor: Colors.white
));

Column的子控件底部居中,左对齐

return Column(
  crossAxisAlignment: CrossAxisAlignment.center,
  mainAxisSize: MainAxisSize.max,
  mainAxisAlignment: MainAxisAlignment.end,
  children: <Widget>[
      //your elements here
  ],
);

交流

老孟Flutter博客地址(近200个控件用法):http://laomengit.com

欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349