Flutter使用小技巧一(持续更新)

Row 直接包裹 TextField 异常:BoxConstraints forces an infinite width

//使用Flexible包裹
Row(
  children: <Widget>[
    Flexible(
      child: new TextField(),
    ),
  ],
),

Expanded、Flexible区别

Flexible是一个控制RowColumnFlex等子组件如何布局的组件。

Flexible组件可以使RowColumnFlex等子组件在主轴方向有填充可用空间的能力(例如,Row在水平方向,Column在垂直方向),但是它与Expanded组件不同,它不强制子组件填充可用空间

RowColumnFlex会被Expanded撑开,充满主轴可用空间。

  • Expanded
    在这里插入图片描述
    代码
  body: new Row(
          children: <Widget>[
            new RaisedButton(
              onPressed: () {
                print('点击红色按钮事件');
              },
              color: const Color(0xffcc0000),
              child: new Text('红色按钮'),
            ),
            new Expanded(
              flex: 1,
              child: new RaisedButton(
                onPressed: () {
                  print('点击黄色按钮事件');
                },
                color: const Color(0xfff1c232),
                child: new Text('黄色按钮'),
              ),
            ),
            new RaisedButton(
              onPressed: () {
                print('点击粉色按钮事件');
              },
              color: const Color(0xffea9999),
              child: new Text('粉色按钮'),
            ),
          ]
      ),
  • Flexible
    在这里插入图片描述
    代码
 body: new Row(
          children: <Widget>[
            new RaisedButton(
              onPressed: () {
                print('点击红色按钮事件');
              },
              color: const Color(0xffcc0000),
              child: new Text('红色按钮'),
            ),
            new Flexible(
              flex: 1,
              child: new RaisedButton(
                onPressed: () {
                  print('点击黄色按钮事件');
                },
                color: const Color(0xfff1c232),
                child: new Text('黄色按钮'),
              ),
            ),
            new RaisedButton(
              onPressed: () {
                print('点击粉色按钮事件');
              },
              color: const Color(0xffea9999),
              child: new Text('粉色按钮'),
            ),
          ]
      ),

Android无法访问http

android11以及以后为了安全Android系统禁用了http的网络请求

需进行如下配置

  1. 创建network_security_config.xml. (res/xml)
  2. 配置 <application android:networkSecurityConfig="@xml/network_security_config">...
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>

IOS无法访问http

./ios/Runner/Info.plist文件中添加如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  ...
  <key>NSAppTransportSecurity</key>
  <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
  </dict>
</dict>
</plist>

获取当前系统Locale

import 'dart:ui';

window.locale

ClipRRect 圆角无效

问题代码:

_item(index) {
  return ClipRRect(
    borderRadius: BorderRadius.circular(8.0),
    child: Container(
      margin: const EdgeInsets.all(8.0),
      color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
      height: 120,
      child: Center(
        child: Text(
          index.toString(),
          textAlign: TextAlign.center,
          style: const TextStyle(fontSize: 18),
        ),
      ),
    ),
  );

分析:由于ClipRRect内部被Container设置了margin导致的,我们将margin去除就会看到圆角,如果非要加个margin则需要外层再包裹个Container,并且color只能有最外层提供。

_item(index) {
  return Container(
    margin: const EdgeInsets.all(8.0),
    child: ClipRRect(
      borderRadius: BorderRadius.circular(8.0),
      child: Container(
        color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
        height: 120,
        child: Center(
          child: Text(
            index.toString(),
            textAlign: TextAlign.center,
            style: const TextStyle(fontSize: 18),
          ),
        ),
      ),
    ),
  );
}

箭头函数不要写多行,否则报错

StatefulWidget 构造传参数

class UserTextField extends StatefulWidget {
  final TextEditingController? controller;
  final Widget? icon;
  final String? hintText;
  final bool obscureText;

  const UserTextField(
      {Key? key,
      this.controller,
      this.icon,
      this.hintText,
      this.obscureText = false})
      : super(key: key);

  @override
  //如果State构造加入参数会提示警告信息:Don't put any logic in createState
  _UserTextFieldState createState() => _UserTextFieldState();
 
}

解决办法:_UserTextFieldState无需在构造中添加参数

  • 只需要使用widget.xx调用即可
class _UserTextFieldState extends State<UserTextField> {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 56.0,
      margin: const EdgeInsets.only(top: 20.0),
      alignment: Alignment.center,
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(7.0), color: Colors.white),
      padding: const EdgeInsets.only(left: 20.0),
      child: TextField(
        controller: widget.controller,
        maxLines: 1,
        //是否是密码
        obscureText: widget.obscureText,
        style: const TextStyle(
            fontSize: 17.0,
            fontFamily: 'QuicksandMedium',
            color: Color(0xFF4b4b4b)),
        decoration: InputDecoration(
          border: InputBorder.none,
          icon: widget.icon,
          hintText: widget.hintText,
        ),
      ),
    );
  }
}

No MediaQuery ancestor could be found?

No MediaQuery ancestor could be found?

void main() => runApp(const MaterialApp(home: HomePage()));

打开一个下载的Flutter module没有android或者.Android文件运行报AndroidManifest.xml could not be found.

  1. 切换到当前目录
  2. 执行flutter create . .表示输出到当前目录

Flutter Android应用启动白屏的解决方案

打开Android主项目 android工程AndroidManifest.xml

在.Mainactivity下新增

<meta-data
    android:name="io.flutter.embedding.android.SplashScreenDrawable"
    android:resource="@mipmap/gradient_bg"/>

Flutter 输入框导致Column溢出

return Scaffold(
      resizeToAvoidBottomInset: false,

Column溢出

嵌套SingleChildScrollView

   body: SingleChildScrollView(
     child: Column(

Cannot use this MethodChannel before the binary messenger has been initialized

channel还未初始化就使用了相应功能,比如SharedPreferences

void main() async {
  WidgetsFlutterBinding.ensureInitialized();//添加
  await ApplicationRouter.setupRouter();
  AppManager.getInstance().isLogin =
      await SpUtils.getBool(AppConstants.spKeyLogin);
  runApp(const MyApp());
}

解决flutter在iOS14+真机上的运行问题

flutter run --release

设置BottomNavigationBar超过3个后,不显示颜色

BottomNavigationBar设置如下属性,这是因为超过3个后type被设置为了BottomNavigationBarType.shifting

type: BottomNavigationBarType.fixed

源码解析:

// Computes the default value for the [type] parameter.
  //
  // If type is provided, it is returned. Next, if the bottom navigation bar
  // theme provides a type, it is used. Finally, the default behavior will be
  // [BottomNavigationBarType.fixed] for 3 or fewer items, and
  // [BottomNavigationBarType.shifting] is used for 4+ items.
  BottomNavigationBarType get _effectiveType {
    return widget.type
      ?? BottomNavigationBarTheme.of(context).type
      ?? (widget.items.length <= 3 ? BottomNavigationBarType.fixed : BottomNavigationBarType.shifting);
  }

多个main函数选择性打包

flutter build apk -t lib/main_dev.dart

flutter build ipa --release -t lib/main_sit.dart

Mac 打包 flutter项目 ipa 打包步骤

1)首先要确认flutter的环境变量已经配置完好
2)在android studio里面更新代码
3)使用 flutter build ios进行打包
4)打包完成以后使用命令直接进入iphoneos文件夹,由于本身android studio 终端就是在项目下面了
,所以就直接 先 cd buildcd iphoneos
当看见终端的前面变成 iphoneos git(master):
5)使用命令 rm -rf Payload
6)最后使用命令

mkdir Payload && mv Runner.app Payload && zip -r Payload.zip Payload && mv Payload.zip Payload.ipa

输入框搜索请求限制

Timer? timer;

void search(Function function, {duration = 500}) {
  timer?.cancel();
  timer = Timer(Duration(milliseconds: duration), () {
    function.call();
    timer = null;
  });
}

InWell、RaisedButton去除点击效果

InkWell(
  onTap: () {},
  child: Text('InkWell 组件'),
    highlightColor: Colors.transparent, // 透明色
    splashColor: Colors.transparent, // 透明色
),

RaisedButton(
  onPressed: () {},
    child: Text('按钮'),
    splashColor: Colors.transparent,
    highlightColor: Colors.transparent,
    highlightElevation: 0, 
    elevation: 0, 
),

去除ListView、SingleChildScrollView、CustomScrollView边界水波纹效果

  • 自定义ScrollBehavior
class AppScrollBehavior extends ScrollBehavior {
  @override
  Widget buildOverscrollIndicator(
      BuildContext context, Widget child, ScrollableDetails details) {
    switch (getPlatform(context)) {
      case TargetPlatform.linux:
      case TargetPlatform.macOS:
      case TargetPlatform.windows:
      case TargetPlatform.iOS:
        return child;
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
        return GlowingOverscrollIndicator(
            showLeading: false,
            showTrailing: false,
            child: child,
            axisDirection: details.direction,
            color: Theme.of(context).colorScheme.secondary);
    }
  }
}
  • 使用:ScrollConfiguration控件包裹
body: ScrollConfiguration(
  behavior: AppScrollBehavior(),
  child: CustomScrollView(
    controller: _controller,
    slivers: [
      _title(),
      _searchStickBar(context),
      _exploreList(context),
    ],
  ),
)

空间适配FittedBox

源码

const FittedBox({
  Key? key,
  this.fit = BoxFit.contain, // 适配方式
  this.alignment = Alignment.center, //对齐方式
  this.clipBehavior = Clip.none, //是否剪裁
  Widget? child,
})

使用
Text仅显示一行,超出后自动适配大小

Padding(
    padding: const EdgeInsets.all(10),
    child: FittedBox(
      child: Row(children: [
        Text('wuxinxi' * 13,style:const  TextStyle(fontSize: 18),)
      ]),
    )),

GridView item设置高度

import 'package:flutter/rendering.dart';

///
/// @date: 2022/5/22 11:58
/// @author: Tangren
/// @remark:
///

class SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight
    extends SliverGridDelegate {
  /// Creates a delegate that makes grid layouts with a fixed number of tiles in
  /// the cross axis.
  ///
  /// All of the arguments must not be null. The `mainAxisSpacing` and
  /// `crossAxisSpacing` arguments must not be negative. The `crossAxisCount`
  /// and `childAspectRatio` arguments must be greater than zero.
  const SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight({
    required this.crossAxisCount,
    this.mainAxisSpacing = 0.0,
    this.crossAxisSpacing = 0.0,
    this.height = 56.0,
  })  : assert(crossAxisCount > 0),
        assert(mainAxisSpacing >= 0),
        assert(crossAxisSpacing >= 0),
        assert(height > 0);

  /// The number of children in the cross axis.
  final int crossAxisCount;

  /// The number of logical pixels between each child along the main axis.
  final double mainAxisSpacing;

  /// The number of logical pixels between each child along the cross axis.
  final double crossAxisSpacing;

  /// The height of the crossAxis.
  final double height;

  bool _debugAssertIsValid() {
    assert(crossAxisCount > 0);
    assert(mainAxisSpacing >= 0.0);
    assert(crossAxisSpacing >= 0.0);
    assert(height > 0.0);
    return true;
  }

  @override
  SliverGridLayout getLayout(SliverConstraints constraints) {
    assert(_debugAssertIsValid());
    final double usableCrossAxisExtent =
        constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
    final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
    final double childMainAxisExtent = height;
    return SliverGridRegularTileLayout(
      crossAxisCount: crossAxisCount,
      mainAxisStride: childMainAxisExtent + mainAxisSpacing,
      crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
      childMainAxisExtent: childMainAxisExtent,
      childCrossAxisExtent: childCrossAxisExtent,
      reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
    );
  }

  @override
  bool shouldRelayout(
      SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight oldDelegate) {
    return oldDelegate.crossAxisCount != crossAxisCount ||
        oldDelegate.mainAxisSpacing != mainAxisSpacing ||
        oldDelegate.crossAxisSpacing != crossAxisSpacing ||
        oldDelegate.height != height;
  }
}

条件导入

场景:当某个包只支持某个平台时(在不支持的平台下编译会不通过)
示例:

//默认导入:iframe_placeholder.dart。如果后面的条件符合则导入iframe_web
export 'iframe_placeholder.dart' if (dart.library.html) 'iframe_web';
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐