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

Flutter使用小技巧二(持续更新),第1张

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

Flutter使用小技巧二(持续更新...) 修改main路径后xcode无法运行Image.asset 设置宽高无效导航返回的拦截手动退出最后一个pageTextField光标问题TextField切换焦点、隐藏默认下划线系统状态栏高度和底部 *** 作栏高度适配问题禁用App置灰App底部导航切换导致重建问题Waiting for another flutter command to release the startup lock设置当前控件大小为父控件大小的百分比

修改main路径后xcode无法运行

mian的路径被修改后ios/Runner.xcodeproj运行失败,需要修改ios/Flutter/Generated.xcconfig配置

// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/Users/sensiwu/Library/Android/FlutterSDK/flutter
FLUTTER_APPLICATION_PATH=/Users/sensiwu/AndroidProject/Tangren/FlutterDemo/multi_language
COCOAPODS_PARALLEL_CODE_SIGN=true
**FLUTTER_TARGET=lib/i18n/main.dart  //main路径**
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=1.0.0
FLUTTER_BUILD_NUMBER=1
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=false
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.packages
Image.asset 设置宽高无效

使用Container包裹Image.asset发现image宽高跟随Container而不是设置的大小,解决办法是:再使用Center包裹Image.asset

Container(
  height: 60.0,
  width: 60.0,
  margin: const EdgeInsets.all(10.0),
  decoration: BoxDecoration(
      color: backgroundColor,
      borderRadius: BorderRadius.circular(10.0)),
  child: Center(
    child: Image.asset(
      iconUrl,
      width: 30,
      height: 30,
    ),
  ),
),
导航返回的拦截

可以使用WillPopScope

const WillPopScope({
  ...
  required WillPopCallback onWillPop,
  required Widget child
})

onWillPop是一个回调函数,当用户点击返回按钮时被调用(包括导航返回按钮及Android物理返回按钮)。该回调需要返回一个Future对象,如果返回的Future最终值为false时,则当前路由不出栈(不会返回);最终值为true时,当前路由出栈退出。我们需要提供这个回调来决定是否退出。

import 'package:flutter/material.dart';

class WillPopScopeTestRoute extends StatefulWidget {
  @override
  WillPopScopeTestRouteState createState() {
    return WillPopScopeTestRouteState();
  }
}

class WillPopScopeTestRouteState extends State<WillPopScopeTestRoute> {
  DateTime? _lastPressedAt; //上次点击时间

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        if (_lastPressedAt == null ||
            DateTime.now().difference(_lastPressedAt!) > Duration(seconds: 1)) {
          //两次点击间隔超过1秒则重新计时
          _lastPressedAt = DateTime.now();
          return false;
        }
        return true;
      },
      child: Container(
        alignment: Alignment.center,
        child: Text("1秒内连续按两次返回键退出"),
      ),
    );
  }
}

对于Dialog也同样适用

 _showBottomDialog(
      BuildContext context, String title, List<SelectContentModel> list) {
        context: context,
        isDismissible: false,
        builder: (context) {
          return WillPopScope(
            onWillPop: () async => false,
            child: WidgetSelectListView(
                name: title,
                ...),
          );
        });
  }
}

手动退出最后一个page
SystemNavigator.pop()
TextField光标问题

如果设置了 controller.text ,并且在 onChanged 发生变化的时候 通过 setState(){} 重新更新 text,那就会使输入框状态发生变化,光标会回到初始位置。

static textEditingController(String text) {
    return TextEditingController.fromValue(TextEditingValue(
        // 设置内容
        text: text ?? "",
        // 保持光标在最后
        selection: TextSelection.fromPosition(TextPosition(
            affinity: TextAffinity.downstream, offset: text == null ? 0 : text.length))));
  }
TextField切换焦点、隐藏默认下划线

切换焦点

如果一个页面有多个输入框,想实现输入结束,点键盘的完成直接去下一个焦点输入,

首先需要给各个输入框设置focusNode,然后完成的回调事件是onEditingComplete

通过FocusScope.of(context).requestFocus(_focusNodePwd);设置新的焦点

隐藏默认下划线

InputDecoration 里 有个 border 将值设为InputBorder.none

系统状态栏高度和底部 *** 作栏高度适配问题

顶部状态栏高度可通过 MediaQueryData.fromWindow(window).padding.top 获取,底部 *** 作栏高度可通过 MediaQueryData.fromWindow(window).padding.bottom 获取,在需要依附底部按钮的地方,一定需要设置 margin bottom *** 作栏高度,否则按钮会被遮挡,在 iPhone 没有 home 键的机型上可以看出来

禁用App

只需在组件的最外层包裹IgnorePointer或者AbsorbPointer组件,_ignoring为变量,可以通过服务器下发,为 true 时,表示禁用。

@override
Widget build(BuildContext context) {
  return IgnorePointer(
    ignoring: _ignoring,
    child: MaterialApp(
      title: 'Flutter Fly',
      theme: ThemeData(
          primaryColor: Colors.white,
          accentColor: Color(0xFF5DC782),
          backgroundColor: Color(0xFFF2F2F2)),
      routes: Routes.routes,
      initialRoute: Routes.home_page,
      debugShowCheckedModeBanner: false,
    ),
  );
}
置灰App

在组件最外层包裹ColorFiltered,使整个 App 变为灰色,包括图片资源。

@override
Widget build(BuildContext context) {
  return ColorFiltered(
    colorFilter: ColorFilter.mode(Colors.grey, BlendMode.color),
    child: MaterialApp(
      title: 'Flutter Fly',
      theme: ThemeData(
          primaryColor: Colors.white,
          accentColor: Color(0xFF5DC782),
          backgroundColor: Color(0xFFF2F2F2)),
      routes: Routes.routes,
      initialRoute: Routes.home_page,
      debugShowCheckedModeBanner: false,
    ),
  );
}
底部导航切换导致重建问题

导致重建的代码

Widget _currentBody;

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: _currentBody,
    bottomNavigationBar: BottomNavigationBar(
      items: <BottomNavigationBarItem>[
        ...
      ],
      onTap: (index) {
        _bottomNavigationChange(index);
      },
    ),
  );
}

_bottomNavigationChange(int index) {
  switch (index) {
    case 0:
      _currentBody = OnePage();
      break;
    case 1:
      _currentBody = TwoPage();
      break;
    case 2:
      _currentBody = ThreePage();
      break;
  }
  setState(() {});
}

使用IndexStack优化

int _currIndex;

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: IndexedStack(
        index: _currIndex,
        children: <Widget>[OnePage(), TwoPage(), ThreePage()],
      ),
    bottomNavigationBar: BottomNavigationBar(
      items: <BottomNavigationBarItem>[
        ...
      ],
      onTap: (index) {
        _bottomNavigationChange(index);
      },
    ),
  );
}

_bottomNavigationChange(int index) {
  setState(() {
      _currIndex = index;
    });
}

pageView的切换导致重建的问题解决办法如下

class DashboardScreen extends GetView<DashboardController> {
  const DashboardScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: PageView(
          controller: controller.pageController,
          onPageChanged: (index) => controller.onChangePage(index),
          children: const [
            KeepAliveWrapper(child: HomeScreen()),
            KeepAliveWrapper(child: CloudScreen())
          ],
        ),
        bottomNavigationBar: Obx(
          () => BottomNavBar(
            currentIndex: controller.selectIndex.value,
            onSelected: (index) => controller.changePage(index),
          ),
        ));
  }
}
class KeepAliveWrapper extends StatefulWidget {
  final bool keepAlive;
  final Widget child;

  const KeepAliveWrapper({Key? key, this.keepAlive = true, required this.child})
      : super(key: key);

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

class _KeepAliveWrapperState extends State<KeepAliveWrapper>
    with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return widget.child;
  }

  @override
  void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
    if (oldWidget.keepAlive != widget.keepAlive) {
      updateKeepAlive();
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  bool get wantKeepAlive => widget.keepAlive;
}
Waiting for another flutter command to release the startup lock

mac/liunx

killall -9 dart

windows

taskkill /F /IM dart.exe

方法2:
删除 flutter SDK 的目录下/bin/cache/lockfile文件

设置当前控件大小为父控件大小的百分比 使用FractionallySizedBox控件获取父控件的大小并乘以百分比:
MediaQuery.of(context).size.width * 0.5

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/989613.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存