草庐IT

Flutter TextField UI 实例 —— 新手礼包

IAM17前端 2023-04-13 原文

大家好,我是17。

新手礼包一共 3 篇文章,每篇都是描述尽量详细,实例讲解,包会!

本篇介绍了 TextField UI 的常见写法,从TextField的尺寸,border,icon,文本到光标,无所不包!

TextField 的尺寸

默认情况下,TextField 的宽度尽量大,高度包含所有内容并加上 padding。TextField 可以通过 constraints 定义自己的尺寸。

下面的代码规定了 TextField 最大宽度为 200。

TextField(
       decoration: InputDecoration(
          constraints: BoxConstraints(maxWidth: 200),
 ));

让 TextFiled 在高度上也尽量大,设置 expands: true,同时还必须设置 maxLines,minLines 为 null。

TextField(
      maxLines: null,
      minLines: null,
      expands: true,
    );

需要注意在高度上的约束必须是有限的,否则报错。

Column(
      children: [
          TextField(expands: true,)
      ],
    );

这个例子执行会报错。解决办法看下面的代码:

Column(
   children: [
     SizedBox(
         height: 200,
         child: TextField(
           expands: true,
           maxLines: null,
           minLines: null,
         )),
     TextField(
       decoration:
           InputDecoration(constraints: BoxConstraints(maxHeight: 200)),
       expands: true,
       maxLines: null,
       minLines: null,
     ),
     Expanded(
         child: TextField(
       expands: true,
       maxLines: null,
       minLines: null,
     ))
   ],
 );

Column 中有三个 TextField,一个用 SizedBox 包起来,一个用 InputDecoration 自带的 constraints 限定,一个用 Expanded 包起来,都是可以的,都可以让 TextField 在高度上的约束是有限的。

除了 SizedBox,很多 widget 都有修改约束的能力,比如 Container,ConstrainedBox 等。

Expanded 的解释可以看这篇 Flutter 弹性布局的基石: Flex 和 Flexible

默认展示

第一个示例给出全部代码,贴到 main.dart 就能运行。后面的只给出 TextField 相关。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: Center(child: SizedBox(width: 300, child: MyWidget()))));
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  var controller = TextEditingController(text: "IAM17");
  
  void dispose() {
    controller.dispose();
    super.dispose();
  } 
  
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        TextField(),
        SizedBox(
          height: 20,
        ),
        TextField(),
      ],
    );
  }
}

没加 Const 关键字,因为反复修改的时候可能得一会删,一会加。发布代码的时候还是应该加上的。

controller 后面会用到

用两个 TextField 是为了方便查看 Focus 的效果。上面的为 正常状态的 TextField,下面的为 focus 状态的 TextField。默认配置正常状态下的 TextField 是一条灰线,有焦点的时候变成蓝色。接下来,我们把他变成想要的样子。

去掉下面的横线

TextField(  
    decoration: InputDecoration(  
        border: InputBorder.none,  
    ),  
),

下面的横线其实就是 border,去掉后,现在只剩下一个光标了。想怎样装扮可以用其它 widget,比如可以用 Container 包起来,自定义 border,也可以用 Row 包起来,加各种图标。这是一个方案,如果你对 TextField 不熟悉,可以这样做来快速完成任务,但实际上,TextField 直接就可以完成大多数装扮,还是用优先用 TextField 自带的装扮为好,因为这样可以少写很多代码。

实际上,在设置 border: InputBorder.none后还是有 padding 的。为了彻底消除 decoration 的影响,可以把它设置为 null。

TextField(
     decoration: null,
);

加边框

TextField(
      decoration: InputDecoration(
        border: OutlineInputBorder(
            gapPadding: 4,
            borderSide: BorderSide(
                color: Colors.green, width: 2, style: BorderStyle.solid),
            borderRadius: BorderRadius.circular(10)),
      ),
    )

我们给 TextField 加上了宽度为 2,圆角为 10 的边框。

  • width 是用来定义边框的宽度的,可以用小数,比如 1.5
  • style 是线框的样式,目前只有一种可选,就是BorderStyle.solid
  • color 是线框的颜色
  • borderRadius 可以定线框的圆角。
  • gapPadding 定义 labelText 左右的 Padding。

前面几个都好理解,gapPadding 我再放一个图就明白了。

gapPadding:4 修改为 100 看下效果。

gapPadding 是作用于 LabelText 的,为 LabelText 的左右添加空白。虽然在视觉上好像是只给右边加了空白,其实左边也加了,只是左边没那么长的线框可以减,看起来好像是少了一小段,其实左边的上边框已经完全减掉了。

label 也可以拆开写,效果是一样的。

 labelText:  'IAM17',
 labelStyle: TextStyle(color:Color(0xFFC45F84),fontSize: 24),

可能你会想到,要用虚线边框怎么办,需要自定义 decration,本文就不展开说了。

如果你已经运行了示例代码,会发现 width,color 没有生效?确实是没有生效,线框的颜色还是默认的灰色,宽度还是 1。

定义线框的颜色和宽度

定义线框的宽度和颜色不能用 border。InputDecoration 按状态还为我们准备了五种 border,下面示范的是最常用的两种,正常状态下的 enabledBorder,和 focus 状态下的 focusedBorder。

TextField(
       decoration: InputDecoration(
         enabledBorder: OutlineInputBorder(
           borderRadius: BorderRadius.circular(5),
           borderSide: BorderSide(
             color: Colors.green,
             width: 1.0,
           ),
         ),
         focusedBorder: OutlineInputBorder(
           borderRadius: BorderRadius.circular(30),
           borderSide: BorderSide(
             color: Color(0xFFC45F84),
             width: 2.0,
           ),
         ),
       ),
     )

第三种是 disabledBorder,看效果需要 enabled: false 禁用 TextField。禁用后会展示灰色 border,无法 focus。

TextField(
    decoration: InputDecoration(
      enabled: false,
      disabledBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(5),
        borderSide: BorderSide(
          color: Colors.grey,
          width: 1.0,
        ),
      ),    
    ),
);

第四种第五种是 error 相关,errorBorder 与 focusedErrorBorder。 给 errorText 赋值,就会触发 TextField 的错误状态。

 TextField(
     decoration: InputDecoration(
       errorBorder: OutlineInputBorder(
         borderSide: BorderSide(
           color: Color.fromARGB(255, 157, 23, 13),
           width: 1.0,
         ),
       ),
       errorText: '出错了!',
       focusedErrorBorder: OutlineInputBorder(
         borderRadius: BorderRadius.circular(5),
         borderSide: BorderSide(
           color: Colors.red,
           width: 2.0,
         ),
       ),
     ),
   )

文本样式,背景色

 TextField(
      controller: controller,
      style: TextStyle(color: Color(0xFFC45F84), fontSize: 24),
      decoration: InputDecoration(
          filled: true, fillColor: Color.fromARGB(255, 192, 241, 218)));

controller 在最开始的代码中已经给出来了var controller = TextEditingController(text: "IAM17"); 现在我们用 controller 显示初始文本。

filled 默认为 false,fillColor 无效,要设置背景色,需要设置 filled: true,然后再设置 fillColor。

正文文本的样式用 style。可以用 textAlign 控制文本的摆放。我们可以把文本摆放在中间。

TextField(
        textAlign: TextAlign.center,
        controller: controller,
        style: TextStyle(color: Color(0xFFC45F84), fontSize: 24),
        decoration: InputDecoration(
            filled: true, fillColor: Color.fromARGB(255, 192, 241, 218)))
  ]));

除了可以摆放在中间,还可以摆在末尾,一共有 5 个值可选,具体可以查看 TextAlign

不够生动?用 icon 和 text 来装扮吧!

 TextField(
       controller: controller,
       style: TextStyle(color: Color(0xFFC45F84), fontSize: 24),
       decoration: InputDecoration(
         icon: Icon(Icons.search),
         prefixIcon: Icon(Icons.account_box),
         prefix: Text('你是谁?',
             style: TextStyle(
                 color: Color.fromARGB(255, 25, 73, 6), fontSize: 20)),
         suffixIcon: Icon(Icons.star),
         suffix: Text('我们见过的',
             style: TextStyle(
                 color: Color.fromARGB(255, 14, 92, 99), fontSize: 20)),
       ))

内容有点多,把最外面的 Container 的宽度放大到 400。

一共有五个位置用来装饰。最前面的是 icon,在 border 的外面。接下来是 prefixIcon,然后是正文,最后是 suffix 和 subffixIcon。

这个五个位置虽然从名字上来看是 Icon 和 Text,但实际上只要是 Widget 都可以!但最好是用 Icon,Text,因为如果用其它 Widget,可能享受不到 Theme 的福利了。

prefix,suffix 也可以用两个字段替代。

prefixText: '你是谁?',
prefixStyle: TextStyle( color: Color.fromARGB(255, 25, 73, 6), fontSize: 20),
suffixText: '我们见过的',
suffixStyle: TextStyle( color: Color.fromARGB(255, 14, 92, 99), fontSize: 20),

扩写和缩写只能采用一种,同时存在会报错!

自定义 Icon 的颜色

当前 Icon 的 color 都是默认的,如何修改 Icon 的颜色呢?可能你第一时间想到这样修改:

icon: Icon(Icons.search,color:Colors.green),

你一定很高兴,it work! 现在 TextField 的正常状态和 foucs 状态的颜色都是 green。那么,如果想让 TextField 的 focus 状态的 icon 颜色是红色,怎么办?

思考中…

好像很棘手,其实 Flutter 已经为我们设计好了如何修改 Icon 的颜色,用 Theme!

首先定义一个 MaterialStateColor。

class IconColor extends MaterialStateColor {
  const IconColor() : super(_defaultColor);
  //绿色
  static const int _defaultColor = 0xff00ff00;
  //红色
  static const int _focusColor = 0xffff0000;

  
  Color resolve(Set<MaterialState> states) {
    if (states.contains(MaterialState.focused)) {
      return const Color(_focusColor);
    }
    return const Color(_defaultColor);
  }
}

然后加入到 Theme 中,我们需要修改一下之前的代码。

MaterialApp(
   theme: ThemeData(
      inputDecorationTheme: InputDecorationTheme(
         iconColor: IconColor()
       ),),
   home: Scaffold(
       body: Center(child: SizedBox(width: 400, child: MyWidget()))));

查看效果默认的时候 icon 是绿色的,focus 的时候是红色的。

如果你觉得定义一个类太麻烦,也可以用 resolveWith 方法

MaterialApp(
    theme: ThemeData(
      inputDecorationTheme: InputDecorationTheme(iconColor:
          MaterialStateColor.resolveWith((Set<MaterialState> states) {
        if (states.contains(MaterialState.focused)) {
          return Colors.red;
        }
        return Colors.green;
      })),
    ),
    home: Scaffold(
        body: Center(child: SizedBox(width: 300, child: MyWidget()))));

前面说的 border, 也可以通过 Theme 设置。这样就不用每个 TextField 都定一遍了!

inputDecorationTheme: InputDecorationTheme(
         enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.green)),
         focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.red))
 )

inputDecorationTheme 可以设置很多内容。Theme 相当于是 css 中的样式表。Theme 如果写在 MaterialApp 中,就相当于是全局样式表了,写在其它地方相当于局部样式表。子级的 Theme 的优先级大于父级的 Theme。写在 Widget 里的相当于 Style,优先级最高。

isDense

上面是默认 isDense:false 的效果,下面是 isDense:true 的效果,就是icon变小了一些。

InputDecoration(
     isDense: true,
     icon: Icon(Icons.search),
     prefixIcon: Icon(Icons.account_box),
     prefix: Text('你是谁?',
         style: TextStyle(
             color: Color.fromARGB(255, 25, 73, 6), fontSize: 20)),
     suffixIcon: Icon(Icons.star),
     suffix: Text('我们见过的',
         style: TextStyle(
             color: Color.fromARGB(255, 14, 92, 99), fontSize: 20)),
   );

hint text 与 helper text

灰色的是 hintText,和 html 中 placeholder 差不多。绿色的是 helper Text,显示在左下角。

TextField(
   decoration: InputDecoration(
     hintText: 'IAM17',
     hintStyle: TextStyle(color: Colors.black54),
     hintMaxLines: 1,
     helperText: '我们见过的',
     helperStyle: TextStyle(color: Color.fromARGB(255, 52, 116, 7)),
     helperMaxLines: 1,
   ))

已经包含 hint text 与 helper text 的所有属性了,比较好理解,就不再解释了。要注意的一点是:focus 对这两个 text 的样式没有影响。error 状态 hint text 没有变化,helper text 被 errorText 取代。

label text

同时有 label text 和 hint text 的时候,正常状态下会优先显示 labelText。在 focus 状态下,labelText 缩小到左上角,hint text 显示出来。

label text 远没有这么简单,除 labelText,labelStyle,还有几个属性需要了解。

floatingLabelStyle 定义 focus 状态下 label 显示在左上角的样式。正常状态下 label text 的颜色用 labelStyle 设置为灰色,浮到左上角后可以用 floatingLabelStyle 设置为绿色。

TextField(
    decoration: InputDecoration(
      labelText: '你是谁',
      labelStyle: TextStyle(color: Colors.grey),
      floatingLabelStyle: TextStyle(color: Colors.green),
    ))
)

floatingLabelAlignment 可以让左上角的 label 显示在中间。(只有 start 和 center 两个选项)

TextField(
   decoration: InputDecoration(
     labelText: '你是谁',
     labelStyle: TextStyle(color: Color.fromARGB(255, 194, 52, 101)),
     floatingLabelStyle: TextStyle(color: Colors.blue),
     floatingLabelAlignment: FloatingLabelAlignment.center
   ));

floatingLabelBehavior 控制 label 的行为,有三个选项

  • FloatingLabelBehavior.auto 默认。正常状态覆盖 hint,focus 状态上浮。
  • FloatingLabelBehavior.always 正常状态 和 focus 状态 都上浮。hint 正常显示。
  • FloatingLabelBehavior.never 正常状态覆盖 hint,focus 状态不上浮。这时就和 hint 并不多了,唯一不同的是 focus 的时候 hint 不消失,label 消失。

padding

默认情况下,在正文的四周是有 padding 的。

contentPadding: EdgeInsets.zero 可以去掉左右的 padding。

decoration: InputDecoration(
    contentPadding: EdgeInsets.zero,
    filled: true,
    fillColor: Color.fromARGB(255, 192, 241, 218)))

去掉上下的 padding 要用到一个属性,isCollapsed可以把上下左右的 padding 都去掉。

InputDecoration(
    isCollapsed: true,
    filled: true,
    fillColor: Color.fromARGB(255, 192, 241, 218))

也可以用InputDecoration.collapsed,但要求必须指定 hintText,不允许再指定 labelText,errorText,icon。

InputDecoration.collapsed(
   hintText: "你是谁",
   filled: true,
   fillColor: Color.fromARGB(255, 192, 241, 218))

直接用 isCollapsed: true,可以指定 labelText,errorText,icon,但 UI 上可能不大理想,所以如果想去掉所有 padding,优先用 InputDecoration.collapsed

自定义光标

可以自定义光标的宽度,radius,和颜色。

TextField(
     cursorWidth: 16.0,
     cursorRadius: Radius.circular(18.0),
     cursorColor: Color(0xFFC45F84),
   ),

Flutter TextField UI 常见写法到这里就结束了,谢谢观看。

欢迎观看下一篇 Flutter TextField 交互实例 —— 新手礼包

有关Flutter TextField UI 实例 —— 新手礼包的更多相关文章

  1. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  2. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  3. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  4. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  5. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  6. ruby-on-rails - 使用 ruby​​ 将多个实例变量转换为散列的更好方法? - 2

    我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。

  7. ruby - 为什么当我调用类的实例方法时,初始化不显示为方法? - 2

    我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认

  8. ruby - 在 Ruby 中,在类方法的上下文中,什么是实例变量和类变量? - 2

    如果我有以下一段Ruby代码:classBlahdefself.bleh@blih="Hello"@@bloh="World"endend@blih和@@bloh到底是什么?@blih是Blah类中的一个实例变量,@@bloh是Blah类中的一个类变量,对吗?这是否意味着@@bloh是Blah的类Class中的一个变量? 最佳答案 人们似乎忽略了该方法是类方法。@blih将是常量Bleh的类Class实例的实例变量。因此:irb(main):001:0>classBlehirb(main):002:1>defself.blehirb

  9. ruby - 从外部访问类的实例变量 - 2

    我理解(我认为)Ruby中类变量和类的实例变量之间的区别。我想知道如何从该类外部访问该类的实例变量。从内部(即在类方法中而不是实例方法中),它可以直接访问,但是从外部,有没有办法做MyClass.class.[@$#]variablename?我没有任何具体原因要这样做,只是学习Ruby并想知道是否可行。 最佳答案 classMyClass@my_class_instance_var="foo"class上述yield:>>foo我相信Arkku演示了如何从类外部访问类变量(@@),而不是类实例变量(@)。我从这篇文章中提取了上述内

  10. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

随机推荐