这节课我们学习 Flutter 开发中最常用的三大基础交互 UI 组件Image图片、TextField输入框、ElevatedButton/TextButton按钮结合上节课的布局知识就能实现带输入、点击、图片展示的完整基础页面比如登录页、注册页、个人信息编辑页也是后续复杂页面的基础组件。课前回顾布局核心Container样式盒子Row/Column线性排列Expanded弹性适配SizedBox间距嵌套原则从外到内搭框架组件可互相嵌套比如 Image 放 Container 里加圆角TextField 和 Button 放 Column 里做登录布局代码仍基于无状态组件StatelessWidget后续会结合有状态组件实现「获取输入值、按钮交互」本节课先掌握组件基础用法和样式。一、图片展示核心Image 组件Flutter 中Image组件支持网络图片、本地图片、资源图片三种主流方式配合Container的decoration可实现圆角、圆形、裁剪等效果是展示图片的唯一核心组件。核心使用场景网络图片接口返回的商品图、头像、banner 图开发中最常用本地图片项目包内的静态图比如 logo、默认占位图圆形 / 圆角图片头像、商品小图的经典样式。1. 最常用网络图片Image.network核心语法dartImage.network( 图片网络地址, // 必传支持http/https width: 100, // 宽可选 height: 100, // 高可选 fit: BoxFit.cover, // 图片适配方式核心必设 );关键属性fit图片适配避免变形表格fit 值作用适用场景BoxFit.cover覆盖容器裁剪超出部分不变形头像、商品图、方形图BoxFit.fill填充容器会拉伸变形背景图、banner 图BoxFit.contain适应容器保持比例留空白logo、图标类图片BoxFit.fitWidth适应容器宽度保持比例横向长图BoxFit.fitHeight适应容器高度保持比例纵向长图2. 本地静态图片Image.asset需要先在项目中配置图片资源两步走步骤 1创建资源目录并放图片在项目根目录创建文件夹images命名小写无空格将图片放入该目录比如avatar.png、logo.jpg打开pubspec.yaml项目配置文件找到flutter节点添加图片资源配置yamlflutter: assets: - images/avatar.png # 单个图片 - images/ # 整个images目录下的所有图片推荐新增图片无需重新配置注意pubspec.yaml对缩进要求严格assets前面是2 个空格-前面也是2 个空格否则会报错步骤 2使用本地图片dartImage.asset( images/avatar.png, // 路径和配置一致 width: 80, height: 80, fit: BoxFit.cover, );3. 实战圆形 / 圆角图片开发高频需求Flutter 中没有直接的「圆形图片属性」需结合ContainerBoxDecorationImage实现这是标准写法适配所有图片类型。1圆角图片dartContainer( width: 100, height: 100, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), // 圆角大小 image: DecorationImage( image: NetworkImage(https://picsum.photos/200/200), // 网络/本地图片都可 fit: BoxFit.cover, // 必须加避免变形 ), ), );2圆形图片头像专用核心BorderRadius.circular值为容器宽高的一半容器必须是正方形dartContainer( width: 80, // 宽高相等 height: 80, decoration: BoxDecoration( shape: BoxShape.circle, // 直接设为圆形替代borderRadius效果一致 image: DecorationImage( image: AssetImage(images/avatar.png), fit: BoxFit.cover, ), ), );技巧用shape: BoxShape.circle无需计算圆角更简洁专门用于圆形图片。4. 图片占位 / 错误处理实战必备网络图片加载时会有空白加载失败会显示红叉需加占位图和错误图推荐使用CachedNetworkImage第三方包比原生 Image 更强大支持缓存步骤如下步骤 1添加依赖打开pubspec.yaml在dependencies节点添加yamldependencies: flutter: sdk: flutter cached_network_image: ^3.3.0 # 最新版本可查pub.dev保存后终端执行flutter pub get下载依赖。步骤 2使用带占位 / 错误的网络图片dartimport package:cached_network_image/cached_network_image.dart; // 先导入 // 替换原生Image.network CachedNetworkImage( imageUrl: https://picsum.photos/200/200, width: 100, height: 100, fit: BoxFit.cover, placeholder: (context, url) Container( // 加载中占位图灰色盒子加载动画 color: Colors.grey[200], child: Center(child: CircularProgressIndicator(strokeWidth: 2)), ), errorWidget: (context, url, error) Container( // 加载失败图灰色盒子错误图标 color: Colors.grey[200], child: Center(child: Icon(Icons.error, color: Colors.grey)), ), );二、用户输入核心TextField 组件TextField是 Flutter 中唯一的输入框组件支持账号、密码、手机号、文本输入可设置占位符、输入类型、样式、边框等是实现「用户和应用交互」的核心组件。核心属性必记高频使用表格属性作用常用值 / 类型decoration输入框装饰占位符、边框、图标核心InputDecoration () 包裹keyboardType键盘类型数字 / 文本 / 手机号TextInputType.text/number/phoneobscureText是否隐藏输入密码框true/false默认 falsetextAlign输入文字对齐TextAlign.left/centerstyle输入文字样式TextStyle(fontSize:16)enabled是否禁用输入框true/false默认 true核心装饰InputDecoration输入框样式重中之重TextField的所有外观样式都在InputDecoration中设置开发中几乎必配核心属性如下dartInputDecoration( hintText: 请输入账号, // 占位符输入前的提示文字 hintStyle: TextStyle(fontSize:14, color: Colors.grey[400]), // 占位符样式 labelText: 账号, // 标签文字点击输入框会浮到左上角 prefixIcon: Icon(Icons.person, color: Colors.blue), // 左侧图标账号/密码图标 suffixIcon: Icon(Icons.clear), // 右侧图标清空/眼睛图标 border: OutlineInputBorder(), // 有边框默认无边框 enabledBorder: OutlineInputBorder( // 未聚焦时的边框 borderSide: BorderSide(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(8), ), focusedBorder: OutlineInputBorder( // 聚焦时的边框高亮 borderSide: BorderSide(color: Colors.blue), borderRadius: BorderRadius.circular(8), ), filled: true, // 开启背景填充 fillColor: Colors.grey[50], // 输入框背景色 contentPadding: EdgeInsets.symmetric(horizontal:16, vertical:14), // 输入框内边距 );实战常用输入框账号 密码框结合布局实现经典的账号和密码输入框直接嵌入 Column 即可用dart// 账号输入框 TextField( decoration: InputDecoration( hintText: 请输入手机号/账号, prefixIcon: Icon(Icons.person, color: Colors.blue), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.grey[200]!), borderRadius: BorderRadius.circular(8), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.blue), borderRadius: BorderRadius.circular(8), ), filled: true, fillColor: Colors.grey[50], ), keyboardType: TextInputType.phone, // 手机号键盘 style: TextStyle(fontSize: 16), ), SizedBox(height: 12), // 间距 // 密码输入框 TextField( decoration: InputDecoration( hintText: 请输入密码, prefixIcon: Icon(Icons.lock, color: Colors.blue), suffixIcon: Icon(Icons.visibility, color: Colors.grey), // 密码眼睛图标 enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.grey[200]!), borderRadius: BorderRadius.circular(8), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.blue), borderRadius: BorderRadius.circular(8), ), filled: true, fillColor: Colors.grey[50], ), obscureText: true, // 隐藏密码 style: TextStyle(fontSize: 16), ),关键注意点输入框默认占满父组件宽度可放在 Container 中指定宽高密码框的「显示 / 隐藏密码」需要结合有状态组件实现后续讲本节课先放静态眼睛图标输入框建议加filled: true和背景色提升视觉体验符合主流 APP 设计风格。三、点击交互核心Button 组件Flutter 中有多种按钮组件开发中最常用的是 3 种ElevatedButton悬浮按钮带背景色、TextButton文字按钮无背景、OutlinedButton轮廓按钮带边框三者用法基本一致核心是点击事件 样式定制。按钮核心共性必传属性onPressed点击事件回调匿名函数后续写交互逻辑子组件通常是Text或Row文字 图标样式定制通过style属性 ButtonStyle实现支持背景色、圆角、大小、文字样式等。1. 主流按钮ElevatedButton带背景色登录 / 确认按钮用基础用法 样式定制dartElevatedButton( onPressed: () { // 点击事件逻辑后续加获取输入值、跳转页面等 print(登录按钮被点击了); }, child: Text(登录), // 按钮文字 style: ElevatedButton.styleFrom( minimumSize: Size(double.infinity, 50), // 宽高占满宽度高度50核心 backgroundColor: Colors.blue, // 背景色 foregroundColor: Colors.white, // 文字/图标颜色 shape: RoundedRectangleBorder( // 圆角 borderRadius: BorderRadius.circular(8), ), elevation: 0, // 阴影高度0无阴影适合扁平设计 textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), // 文字样式 ), );2. 文字按钮TextButton无背景取消 / 忘记密码用基础用法 样式定制dartTextButton( onPressed: () { print(忘记密码被点击了); }, child: Text(忘记密码), style: TextButton.styleFrom( foregroundColor: Colors.grey[600], // 文字颜色 textStyle: TextStyle(fontSize: 14), padding: EdgeInsets.zero, // 取消默认内边距 ), );3. 轮廓按钮OutlinedButton带边框注册 / 其他操作用基础用法 样式定制dartOutlinedButton( onPressed: () { print(注册按钮被点击了); }, child: Text(立即注册), style: OutlinedButton.styleFrom( minimumSize: Size(double.infinity, 50), foregroundColor: Colors.blue, // 文字/边框颜色 side: BorderSide(color: Colors.blue), // 边框颜色和宽度 shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), );小技巧带图标的按钮按钮子组件可放Row实现「图标 文字」按钮适配所有按钮类型dartElevatedButton( onPressed: () {}, child: Row( mainAxisAlignment: MainAxisAlignment.center, // 图标文字居中 children: [ Icon(Icons.login, size: 18), SizedBox(width: 8), Text(登录), ], ), style: ElevatedButton.styleFrom( minimumSize: Size(double.infinity, 50), backgroundColor: Colors.blue, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), );四、综合实战完整登录页面ImageTextFieldButton 布局结合前两节课的布局知识和本节课的 UI 组件实现一个企业级标准登录页包含「顶部 logo 账号密码输入框 登录按钮 忘记密码 注册按钮」直接复制可运行稍作修改即可用于实际项目。完整代码注释超详细可直接复用dartimport package:flutter/material.dart; // 如需用缓存图片导入cached_network_image包 // import package:cached_network_image/cached_network_image.dart; void main() runApp(MyApp()); class MyApp extends StatelessWidget { override Widget build(BuildContext context) { return MaterialApp( title: Flutter登录页, theme: ThemeData(primarySwatch: Colors.blue, elevation: 0), home: LoginPage(), debugShowCheckedModeBanner: false, // 隐藏右上角debug标签 ); } } class LoginPage extends StatelessWidget { override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, // 主体SingleChildScrollView防止键盘弹出时溢出 body: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 30, vertical: 80), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // 顶部logo圆形图片 Container( width: 100, height: 100, decoration: BoxDecoration( shape: BoxShape.circle, image: DecorationImage( image: NetworkImage(https://picsum.photos/200/200), fit: BoxFit.cover, ), boxShadow: [ BoxShadow(color: Colors.blue.withOpacity(0.2), blurRadius: 10) ], ), ), SizedBox(height: 40), // 页面标题 Text( 欢迎登录Flutter应用, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87, ), ), SizedBox(height: 30), // 账号输入框 TextField( decoration: InputDecoration( hintText: 请输入手机号/账号, prefixIcon: Icon(Icons.person, color: Colors.blue), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.grey[200]!), borderRadius: BorderRadius.circular(8), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.blue), borderRadius: BorderRadius.circular(8), ), filled: true, fillColor: Colors.grey[50], contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), keyboardType: TextInputType.phone, style: TextStyle(fontSize: 16), ), SizedBox(height: 12), // 密码输入框 TextField( decoration: InputDecoration( hintText: 请输入密码, prefixIcon: Icon(Icons.lock, color: Colors.blue), suffixIcon: Icon(Icons.visibility, color: Colors.grey[400]), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.grey[200]!), borderRadius: BorderRadius.circular(8), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.blue), borderRadius: BorderRadius.circular(8), ), filled: true, fillColor: Colors.grey[50], contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), obscureText: true, style: TextStyle(fontSize: 16), ), SizedBox(height: 10), // 忘记密码居右 Align( alignment: Alignment.centerRight, child: TextButton( onPressed: () { print(点击了忘记密码); }, child: Text( 忘记密码, style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), style: TextButton.styleFrom(padding: EdgeInsets.zero), ), ), SizedBox(height: 20), // 登录按钮 ElevatedButton( onPressed: () { print(点击了登录按钮); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.login, size: 18), SizedBox(width: 8), Text(登录), ], ), style: ElevatedButton.styleFrom( minimumSize: Size(double.infinity, 50), backgroundColor: Colors.blue, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), elevation: 0, textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), SizedBox(height: 30), // 注册按钮轮廓按钮 OutlinedButton( onPressed: () { print(点击了注册按钮); }, child: Text(立即注册), style: OutlinedButton.styleFrom( minimumSize: Size(double.infinity, 50), foregroundColor: Colors.blue, side: BorderSide(color: Colors.blue), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), ], ), ), ); } }运行效果一个符合主流 APP 设计的登录页顶部圆形 logo 带轻微阴影下方是登录标题账号 / 密码输入框带蓝色左侧图标、圆角、浅灰色背景聚焦时边框高亮忘记密码居右登录按钮是「图标 文字」的蓝色悬浮按钮注册按钮是蓝色轮廓按钮用SingleChildScrollView包裹主体解决键盘弹出时页面溢出的问题实战必备隐藏了 debug 标签更贴近实际应用。实战必备知识点SingleChildScrollView包裹可滚动内容防止键盘弹出时页面超出屏幕所有带输入框的页面都要加Align单独控制组件的对齐方式比如忘记密码居右默认 Column 是左对齐debugShowCheckedModeBanner: false隐藏右上角的 debug 标签发布前必加按钮 minimumSize设置Size(double.infinity, 50)让按钮占满父组件宽度适配所有屏幕。五、本节课核心总结必背Image网络图片用Image.network本地图片用Image.asset需配置 pubspec.yaml圆形 / 圆角图片用ContainerBoxDecoration实现网络图片建议用CachedNetworkImage加占位 / 错误处理TextField所有样式在InputDecoration中设置必配enabledBorder/focusedBorder边框、filled背景密码框加obscureText: true输入框外包裹SingleChildScrollView防止溢出Button分ElevatedButton带背景、TextButton文字、OutlinedButton轮廓必配onPressed点击事件用styleFrom定制样式minimumSize设置按钮宽高组件嵌套UI 组件可和布局组件自由嵌套比如 Image 放 Container 加样式TextField/Button 放 Column 做纵向排列Align 单独控制组件对齐实战细节带输入框的页面必须加SingleChildScrollView按钮建议做扁平设计elevation: 0符合现代 APP 设计风格。六、课后练习敲代码巩固重点是组件嵌套和样式基础练习修改登录页的 logo 为本地图片替换网络图片地址修改按钮背景色为橙色进阶练习在登录页添加「验证码输入框」键盘类型为数字右侧加「获取验证码」TextButton实战练习实现一个个人信息编辑页包含「圆形头像可点击 昵称输入框 手机号输入框禁用 保存按钮」用 Row/Column 布局排版。