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

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

修改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文件

设置当前控件大小为父控件大小的百分比

  1. 使用FractionallySizedBox控件
  2. 获取父控件的大小并乘以百分比:
MediaQuery.of(context).size.width * 0.5
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
flutter_downloader是一个用于在Flutter应用程序中进行文件下载的插件。它提供了一种简单而强大的方式来管理和监控文件下载任务。 使用flutter_downloader,你可以轻松地添加文件下载功能到你的Flutter应用程序中。它支持多个平台,包括AndroidiOS,并且提供了一组易于使用的API来管理下载任务。 要使用flutter_downloader,首先需要在你的Flutter项目中添加依赖。在pubspec.yaml文件中,添加以下内容: ``` dependencies: flutter_downloader: ^1.6.0 ``` 然后运行`flutter pub get`命令来获取依赖。 接下来,在你的代码中导入flutter_downloader包,并使用它来创建和管理下载任务。你可以使用`FlutterDownloader.enqueue`方法来创建一个下载任务,并传递下载链接、保存路径等参数。你还可以使用`FlutterDownloader.open`方法来打开已下载的文件。 以下是一个简单的示例代码,演示了如何使用flutter_downloader进行文件下载: ```dart import 'package:flutter/material.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('File Downloader'), ), body: Center( child: RaisedButton( child: Text('Download File'), onPressed: () { _startDownload(); }, ), ), ), ); } void _startDownload() async { String url = 'https://example.com/file.pdf'; String savedDir = '/storage/emulated/0/Download'; await FlutterDownloader.enqueue( url: url, savedDir: savedDir, showNotification: true, openFileFromNotification: true, ); } } ``` 这是一个简单的示例,当用户点击按钮时,会触发文件下载任务。下载的文件将保存在指定的目录中,并且会显示下载通知。用户可以通过通知来打开已下载的文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴唐人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值