正常情况下,我们使用 TextField/TextFormField 的时候,都是单行的表单或者指定行数的情况。但是有时候,我们希望能自适应父容器的高度,或者高度动态自适应变化。
那么我们应该怎么操作呢?
正常情况下,我们使用 TextField/TextFormField 的时候,都是单行的表单或者指定行数的情况。但是有时候,我们希望能自适应父容器的高度,或者高度动态自适应变化。
那么我们应该怎么操作呢?
AspectRatio 的作用是根据设置调整子元素 child 的宽高比。
AspectRatio 首先会在布局限制条件允许的范围内尽可能的扩展,widget 的高度是由宽度和比率决定的,类似于 BoxFit 中的 contain,按照固定比率去尽量占满区域。
如果在满足所有限制条件过后无法找到一个可行的尺寸,AspectRatio 最终将会去优先适应布局限制条件,而忽略所设置的比率。
常见属性:
1. aspectRatio 宽高比。页面最终也许不会按这个值去布局, 具体则要看综合因素,这只是一个参考值;
2. child 子组件。值的类型为Widget;
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import 'package:flutter/material.dart'; void main(){ runApp(MyApp()); } // 抽离成一个单独的组件 class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( // 导航条 appBar:AppBar(title:Text('Flutter App')), // 主体 body:HomeContent(), ), // 主题 theme: ThemeData(primarySwatch:Colors.yellow), ); } } // AspectRatio组件的用法 class HomeContent extends StatelessWidget{ @override Widget build(BuildContext context) { return AspectRatio( // 高度与宽度的比值 aspectRatio: 5.0/1.0 , child:Container( color:Colors.red ) ); } } |
Container是一个拥有绘制、定位、调整大小的 widget,是开发中最常用、最基础的组件。虽然最基础但不可小觑,熟悉每一个属性可以帮助我们更好更快的实现想要的效果,避免走弯路,也能避免代码冗余。本文主要针对其属性进行讲解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Container({ Key? key, this.alignment, this.padding, this.color, this.decoration, this.foregroundDecoration, double? width, double? height, BoxConstraints? constraints, this.margin, this.transform, this.transformAlignment, this.child, this.clipBehavior = Clip.none, }) |
key用于控制控件如何取代树中的另一个控件,即若widget指定了相同的key,则这些widget可以复用。 如果widget的key值不为空,会判断key._currentElement值所指向的widget,和当前widget的类型key都相同,那么就从旧的父节点上移除,作为当前的节点的子widget之一, 否则将进行真实的创建新的Element。若开发中对组建的复用没有较高要求,一般不设置该属性。
Riverpod AsyncNotifier may sound a litle confusing if you are just coming to Riverpod. After all Riverpod and the latest versions they introduced many concepts. AsyncNotifier works with AsyncNotifierProvider. AsyncNotifier is a wrapper around your state or data which works asyncronomously(await and async).
用户可以在 Windows 10 和 Windows 11 计算机上使用 winget 命令行工具来发现、安装、升级、删除和配置应用程序。 此工具是 Windows 程序包管理器服务的客户端接口。
Windows 程序包管理器 winget 命令行工具作为应用安装程序的一部分在 Windows 11 和现代版本的 Windows 10 上提供。
可以从 Microsoft Store 获取应用安装程序。 如果已安装,请确保已将其更新为最新版本。
备注
winget 命令行工具仅在 Windows 10 1709(版本 16299)或更高版本上受支持。 在你首次以用户身份登录 Windows(这会触发 Microsoft Store 将 Windows 程序包管理器注册为异步进程的一部分)之前,winget 工具不可用。 如果最近已经以用户身份进行了首次登录,但发现 winget 尚不可用,则可以打开 PowerShell 并输入以下命令来请求此 winget 注册:
Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
。
WinGet 包含在 Windows 应用安装程序中。 要试用最新的 Windows 程序包管理器功能,可以通过以下方式之一安装预览版:
下载最新的 winget 预览版。 阅读 winget 预览版发行说明,了解任何新功能。 安装此包将为你提供 WinGet 客户端预览版,但它不会从 Microsoft Store 中启用新预览版的自动更新。
使用 Microsoft 帐户 (MSA)、工作、学校或 Azure Active Directory (AAD) 帐户注册 Windows 预览体验成员开发频道。 Windows 预览体验成员开发频道包括 Microsoft Store 中新预览版的自动更新。
使用 Microsoft 帐户 (MSA) 注册 Windows 程序包管理器预览体验计划。 在添加你的 Microsoft 帐户 (MSA) 后(在你收到电子邮件通知后几天),你将收到 Microsoft Store 中新预览版的自动更新。
Windows 沙盒提供了一个轻型桌面环境,可以安全地独立运行应用程序。 安装在 Windows 沙盒环境中的软件保持“沙盒”状态,并独立于主机运行。 Windows 沙盒不包含 winget,也不包含 Microsoft Store 应用,因此你需要从 GitHub 上的 winget 版本页下载最新的 winget 包。
要在 Windows 沙盒上安装 winget 的稳定版本,请从 Windows PowerShell 命令提示符执行以下步骤:
1 2 3 4 5 6 7 8 |
$progressPreference = 'silentlyContinue' Write-Information "Downloading WinGet and its dependencies..." Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -OutFile Microsoft.VCLibs.x64.14.00.Desktop.appx Invoke-WebRequest -Uri https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.7.3/Microsoft.UI.Xaml.2.7.x64.appx -OutFile Microsoft.UI.Xaml.2.7.x64.appx Add-AppxPackage Microsoft.VCLibs.x64.14.00.Desktop.appx Add-AppxPackage Microsoft.UI.Xaml.2.7.x64.appx Add-AppxPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle |
如果需要程序包管理器的预览版或其他版本,请转到 https://github.com/microsoft/winget-cli/releases。 复制你需要的版本 URL 并更新上述 URI。
有关 Windows 沙盒的详细信息,包括如何安装沙盒以及使用沙盒的预期结果,请参阅 Windows 沙盒文档。
安装程序的行为可能会有所不同,具体取决于你是否是以管理员权限运行 winget。
问题:Launching lib\main.dart on ELE AL00 in debug mode...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Running Gradle task 'assembleDebug'... Exception in thread "main" java.net.ConnectException: Connection timed out: connect at java.net.DualStackPlainSocketImpl.connect0(Native Method) at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:666) at sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:173) at sun.net.NetworkClient.doConnect(NetworkClient.java:180) at sun.net.www.http.HttpClient.openServer(HttpClient.java:463) at sun.net.www.http.HttpClient.openServer(HttpClient.java:558) at sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264) at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191) at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1156) at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177) at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1564) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:263) at org.gradle.wrapper.Download.downloadInternal(Download.java:58) at org.gradle.wrapper.Download.download(Download.java:44) at org.gradle.wrapper.Install$1.call(Install.java:61) at org.gradle.wrapper.Install$1.call(Install.java:48) at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65) at org.gradle.wrapper.Install.createDist(Install.java:48) at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128) at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61) Exception: Gradle task assembleDebug failed with exit code 1 |
解决方案:
1、调整项目空间中的gradle-wrapper.properties文件中的URL,改为你本地最新的gradle版本就好
如:
1 |
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip |
2、调整Android包中的build.gradle文件中的kotlin版本号:跟本地kotlin版本同步就行
如:ext.kotlin_version = '1.3.50'
1 2 3 |
Failed to build intl_utils:generate: ../../../.pub-cache/hosted/pub.flutter-io.cn/watcher-1.0.2/lib/src/constructable_file_system_event.dart:7:57: Error: The class 'FileSystemEvent' can't be extended, implemented, or mixed in outside of its library because it's a sealed class. abstract class _ConstructableFileSystemEvent implements FileSystemEvent { |
请尝试
1 |
dart pub upgrade |
命令,如果还是报错
1 |
dart pub cache repair |
这样我们就可以使用到最新的watcher-1.1.0版本,就不会出现上面这个报错了。
在我们上一篇文章中对Provider
进行了介绍以及类结构的说明,最后还写了一个简单的示例,通过上一章节我们对Provider
有了一个基本的了解,这一章节我们来说说Provider
的8种提供者以及他们的使用区别。
Provider
是最基本的Provider组件,可以使用它为组件树中的任何位置提供值,但是当该值更改的时候,它并不会更新UI,下面我们给出一个示例
1 2 3 4 5 6 7 8 |
class UserModel { String name = "Jimi"; void changeName() { name = "hello"; } } |
1 2 3 4 5 6 7 |
return Provider<UserModel>( create: (_) => UserModel(), child: MaterialApp( debugShowCheckedModeBanner: false, home: ProviderExample(), ), ); |
关于Consumer
后面将消费者在提及,我们这里只需要知道有两个消费者,第一个用于展示模型的数据,第二个用于改变模型的数据。
Comsumer
是用于读取模型的数据name
Consumer
用于改变模型的数据name
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/provider_example/user_model.dart'; import 'package:provider/provider.dart'; class ProviderExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("ProviderExample"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer<UserModel>( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer<UserModel>( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("改变值"), ), ); }, ), ], ), ), ); } } |
状态管理一直是 Flutter 开发中一个火热的话题。谈到状态管理框架,社区也有诸如有以 Get、Provider 、Riverpod 为代表的多种方案,它们有各自的优缺点。面对这么多的选择,你可能会想:「我需要使用状态管理么?哪种框架更适合我?」本文将从作者的实际开发经验出发,分析状态管理解决的问题以及思路,希望能帮助你做出选择。
首先,为什么需要状态管理?根据笔者的经验,这是因为 Flutter 基于 声明式 构建 UI ,使用状态管理的目的之一就是解决「声明式」开发带来的问题。
「声明式」开发是一种区别于传原生的方式,所以我们没有在原生开发中听到过状态管理,那如何理解「声明式」开发呢?
随着 Flutter 的发展,这些年 Flutter 上的状态管理框架如“雨后春笋”般层出不穷,而近一年以来最受官方推荐的状态管理框架无疑就是 Riverpod
,甚至已经超过了 Provider
,事实上 Riverpod
官方也称自己为 “Provider
,但与众不同”。
Provider
本身用它自己的话来说是 “InheritedWidget
的封装,但更简单且复用能力更强。” ,而Riverpod
就是在Provider
的基础上重构了新的可能。
关于过去一年状态管理框架的对比可以看 《2021 年的 Flutter 状态管理:如何选择?》 , 本文主要是带你解剖 RiverPod
的内部是如何实现,理解它的工作原理,以及如何做到比 Provider
更少的模板和不依赖 BuildContext
。