Flutter基础组件

204 阅读4分钟

Flutter 的 UI 渲染是基于一系列组件完成,这里从基础简单的组件开始学习,重点熟悉其常用属性方法,后期会不断完善部分不常用用的属性。

FlutterLogo

image.png

FlutterLogo(
  duration: Duration(microseconds: 1000),
  //textColor: Colors.red,
  size: 200,
  style: FlutterLogoStyle.stacked,
),

Text

Column(
    children: [
        Text(text1,textAlign: 
            TextAlign.left, //左对齐
        ),

        Text(text2,
           maxLines: 2, //最多2行展示
        ),

        Text(text3,
          textAlign: TextAlign.left, //文本不足一行时对齐式没有意义
          style: const TextStyle(
            color: Colors.red,
            fontSize: 18,
            height: 1.2, //行高因子行高=fontSize*height
            decoration: TextDecoration.underline,
            backgroundColor: Colors.yellow),
       ),
  ],
),

文字间距

Text("第五段测试文字 Word",
      style: TextStyle(
        fontSize: 24,
        letterSpacing: 1, //字符间距
        wordSpacing: 4, //单词间距
        height: 1.5,
    )), 

文字阴影

 Text("第六段测试文字",
      style: TextStyle(
        fontSize: 22, 
        height: 1.5, 
        shadows: [
          //文字阴影可以叠加多个
          BoxShadow(offset: Offset(2, 2), color: Colors.red, blurRadius: 8),
          BoxShadow(offset: Offset(-2, -2), color: Colors.green, blurRadius: 8)
       ]
     )),

富文本


Text.rich(TextSpan(children: [
    TextSpan(text: "头部文字", style: TextStyle(color: Colors.red)),
    TextSpan(text: "尾部文字", style: TextStyle(color: Colors.green, fontSize: 20))

])),

图片

通过Image组件来加载并显示图片,Image的数据源可以是asset、文件、内存以及网络.

ImageProvider 是一个抽象类,主要定义了图片数据获取的接口load(),从不同的数据源获取图片需要实现不同的ImageProvider ,如AssetImage是实现了从Asset中加载图片的 ImageProvider,而NetworkImage 实现了从网络加载图片的 ImageProvider

  • 本地图片
Image(image:AssetImage("assets/images/news/news_list_clicked@2x.png"));

Image.asset("assets/images/news/news_list_clicked@2x.png");
  • 网络图片

图片只会小于给定尺寸,但为避免失真图片周边会留白不会根据给定尺寸放大

Image(image: 
  NetworkImage("https://avatars2.githubusercontent.com/u/20411648?s=460&v=4"),
  width: 100,
),

Image.network(
   "https://avatars2.githubusercontent.com/u/20411648?s=460&v=4",
   width: 100,
),
  • FadeInImage 加载图片时需要占位图片,并在图片加载完成后无缝切换展示,就要用FadeInImage
FadeInImage(
    placeholder: AssetImage('xxxx'),
    image: NetworkImage("xxxx")
)

FadeInImage.assetNetwork(
    placeholder: "xxx",
    image: 'xxx'
 )

一般会考虑使用第三方库cached_network_image

CachedNetworkImage(

   //进度条占位 线性进度条LinearProgressIndicator 
   placeholder: (context, url) => CircularProgressIndicator(),
   //本地占位图
   placeholder: (context, url) => Image.asset(imageName), 
   //请求错误后显示
   errorWidget: (context, url, error) => Image.asset(imageName),
   width: 200, 
   height: 200, 
   imageUrl: imageUrl,
 );
  • 字体图标
Icon(Icons.back_hand),
Icon(CupertinoIcons.refresh), 
Icon(Icons.share), 
Icon(CupertinoIcons.share), 
Icon(Icons.camera), 
Icon(CupertinoIcons.camera)],

Text("\uE03e",
    style: TextStyle(
        fontFamily: "MaterialIcons",
        fontSize: 24.0,
        color: Colors.green)
),

单选开关和复选框

image.png

组件本身不会保存当前选中状态,选中状态都是由父组件来管理的

Checkbox的大小是固定的,无法自定义,而Switch只能定义宽度,高度也是固定的

CupertinoSwitch(
  value: _switchSelected,
  activeColor: Colors.orange,
  onChanged: (isOpen) {
    _switchSelected = !_switchSelected;
    setState(() {});
}),

Switch(
  value: _switchSelected,
  activeColor: Colors.red,
  onChanged: (isOpne) {
    _switchSelected = !_switchSelected;
    setState(() {});
}),

Checkbox(
  value: _checkboxSelected,
  activeColor: Colors.blue,
  onChanged: (isCheck) {
     _checkboxSelected = !_checkboxSelected;
    setState(() {});
}),

按钮

image.png

ElevatedButton(
  child: Text("normal"),
  //label: Text("发送"),
  onPressed: () {},
),

TextButton(
  child: Text("submit"),
  onPressed: () {},
),

OutlinedButton(
  onPressed: (){},
  child: Text("outline"));

IconButton(
  icon: Icon(Icons.back_hand),
  onPressed: () {},
)

button样式需要根据不同的状态进行设置,

Container(
    //相设置一个左右边距40的按钮,先设置外边
    margin: EdgeInsets.only(top: 10, left: 40, right: 40),
    
    //再设置一个紧约束,并且宽度为无限大
    child: SizedBox(
    
        width: double.infinity,
        height: 40,
        child: ElevatedButton(
           
           onPressed: () {},
           
           //样式设置<MaterialStateProperty>
           style: ButtonStyle(
                
                //不区分状态统一设置
                //backgroundColor: MaterialStateProperty.all(const Color(0xffFCB605)),
                
                //根据不同的状态设置不同颜色
                backgroundColor: MaterialStateProperty.resolveWith((states) {
                
                    if (states.contains(MaterialState.pressed)) {
                        return Colors.deepPurple;
                    } else {
                        return Colors.green;
                    }
               },),

               //前景色
               foregroundColor: MaterialStateProperty.resolveWith((states) {
                    if (states.contains(MaterialState.pressed)) {
                        return Colors.deepPurple;
                    } else {
                         return Colors.red;
                    }
                },),

 
            //设置水波纹颜色
            //overlayColor: MaterialStateProperty.all(Colors.yellow),

            //设置阴影 不适用于这里的TextButton
            //elevation: MaterialStateProperty.all(10),

  
            //设置边框
            side: MaterialStateProperty.all(BorderSide(color: Colors.red, width: 1)),

            //外边框装饰
            //shape: MaterialStateProperty.all(StadiumBorder()),
            shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius:BorderRadius.circular(20))),

     ),

     child: const Text(

        "登 录",
        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
       ),

   ),

  ),
);

button样式快速设置

SizedBox(
  height: 45,
  width: 200,
  child: ElevatedButton(
    onPressed: () {},
    style: ElevatedButton.styleFrom(
        elevation: 10, shape: const StadiumBorder()),
    child: const Center(child: Text('Elevated Button')),
  ),
)

image.png

SizedBox(
  height: 45,
  width: 200,
  child: ElevatedButton(
    onPressed: () {},
    style: ElevatedButton.styleFrom(
        elevation: 10, shape: const StadiumBorder()),
    child: const Center(child: Text('Elevated Button')),
  ),
);

image.png

进度指示器

image.png

//进度条显示50%
LinearProgressIndicator(
  backgroundColor: Colors.grey[200],
  valueColor: AlwaysStoppedAnimation(Colors.blue),
  value: .5, 
)

//进度条显示50%,会显示一个半圆
CircularProgressIndicator(
  backgroundColor: Colors.grey[200],
  valueColor: AlwaysStoppedAnimation(Colors.blue),
  value: .5,
),

输入框

image.png

TextField(
    
    //先设置控制器
    controller: textController,

    //装饰
    decoration: InputDecoration(

        //左侧外图标
        icon: const Icon(Icons.lock),

        //左侧内图档
        // prefix:   //前缀
        // prefixText: "前缀文字",
        prefixIcon: const Icon(Icons.person),

        //右侧图片后缀
        // suffix:,
        // suffixText: ,
        // suffixIcon: Icon(Icons.clear, color: Colors.red),
        suffixIcon: IconButton(
        
            icon: const Icon(Icons.clear),
            onPressed: () {   
               textController.clear();
            },
            style: ButtonStyle(),
        ),
        
        //填充 false 时填充颜色无效
        filled: false,
        fillColor: Colors.red,
        
        //边框
        enabledBorder: InputBorder.none, 
        focusedBorder: InputBorder.none,

  


        // 未获得焦点下划线设为灰色
        // enabledBorder: UnderlineInputBorder(
            // borderSide: BorderSide(color: Colors.grey),
        // ),

        //获得焦点下划线设为蓝色
        // focusedBorder: UnderlineInputBorder(
            // borderSide: BorderSide(color: Colors.blue),
        // ),


        //获得焦点时边框颜色
        // focusedBorder: OutlineInputBorder(
            // borderSide: BorderSide(color: Colors.orange),
            // borderRadius: BorderRadius.all(Radius.circular(20)),
       // ),
       
       //未获得焦点时边框颜色
       // enabledBorder: OutlineInputBorder(
           // borderSide: BorderSide(color: Colors.red),
           // borderRadius: BorderRadius.all(Radius.circular(20)),
       // ),

 
       //悬浮提示文字
       // labelText: " ",
       // labelStyle: TextStyle(color: Colors.red),
      
       //内间距,内间距调整会更改输入框高度
       // contentPadding: const EdgeInsets.all(0),
      
       isDense: false,
       hintText: "提示文字 ",
       hintStyle: const TextStyle(color: Color(0xff999999)),
  ),

   //正在编辑文字的样式
   style: const TextStyle(
       
       color: Color.fromARGB(255, 80, 68, 68),
       fontWeight: FontWeight.bold,
   ),

    
    //text 字符串 number 数字 phone 手机号url 链接 emailAddress邮箱 datetime 时间
    keyboardType: TextInputType.text,

  
    //完成按钮样式
    textInputAction: TextInputAction.search,

    autofocus: false, //是否自动获取取焦点
    obscureText: false, //是否加密展示
    maxLines: 1, //最大展示行数,默认1

    //最大字符数,设置后输入框下面会有一定间距放置counter
    // maxLength: 11,
    // maxLengthEnforcement: MaxLengthEnforcement.enforced,

  
     //长按或鼠标右击时出现的菜单
     toolbarOptions: const ToolbarOptions(copy: false, paste: false),

     onChanged: (value) {},
     onEditingComplete: () {},
     onSubmitted: (value) {},

     //输入格式
    inputFormatters: [
        //只允许输入数字
        //FilteringTextInputFormatter.digitsOnly,
     ],
     
     //是否可用
     enabled: true,
     
     // cursorColor: ColorRes.public_FFFF0035, //光标颜色
     // cursorHeight: 22, //光标高度
     // cursorWidth: 5, //光标宽度

)

另外关于计数器,可以进行重构

//没有最大字数限制,但希望显示counter,可以设置最大字数为-1
//有字数限制,但不希望显示counter,可以重构build 方法,返回值为Container()或空。

TextField(maxLength: 10,
    buildCounter: (context, {int ? currentLength, bool? isFocused, int? maxLength}) {

        return Container();
    },
),