当前位置: 首页 > web >正文

面试复习题-Flutter场景题

🌟 场景题 1:实现一个“下拉刷新 + 上拉加载更多”的商品列表

需求

  • 商品列表支持下拉刷新。
  • 滑动到底部自动加载更多。
  • 加载中显示“加载中...”Footer。
  • 加载失败显示“点击重试”按钮。
  • 网络请求失败有重试机制。
✅ 高级回答要点:

dart

深色版本

class ProductList extends StatefulWidget {@override_ProductListState createState() => _ProductListState();
}class _ProductListState extends State<ProductList> {final ProductBloc _bloc = ProductBloc();final ScrollController _scrollController = ScrollController();@overridevoid initState() {_bloc.fetchProducts();_scrollController.addListener(_onScroll);super.initState();}void _onScroll() {if (_scrollController.position.pixels >=_scrollController.position.maxScrollExtent - 200) {_bloc.loadMore();}}@overrideWidget build(BuildContext context) {return RefreshIndicator(onRefresh: () => _bloc.refresh(),child: BlocBuilder<ProductBloc, ProductState>(bloc: _bloc,builder: (context, state) {if (state is ProductLoading && state.products.isEmpty) {return const Center(child: CircularProgressIndicator());}return ListView.builder(controller: _scrollController,itemCount: state.products.length + (state.hasMore ? 1 : 0),itemBuilder: (context, index) {if (index == state.products.length) {if (state.loadFailed) {return ElevatedButton(onPressed: _bloc.retryLoadMore,child: const Text('点击重试'),);}return const Padding(padding: EdgeInsets.all(16),child: Center(child: CircularProgressIndicator()),);}return ProductItem(product: state.products[index]);},);},),);}@overridevoid dispose() {_bloc.dispose();_scrollController.dispose();super.dispose();}
}
💡 考察点:
  1. 状态管理:使用 Bloc 或 Riverpod 管理复杂状态(加载中、失败、是否有更多)。
  2. 性能ListView.builder 懒加载。
  3. 用户体验:Footer 显示加载/失败状态。
  4. 内存管理ScrollController 和 Bloc 正确释放。
  5. 可维护性:逻辑与 UI 分离。

🌟 场景题 2:实现一个“登录页”,支持手机号、密码、验证码登录,且表单验证

需求

  • 手机号输入框(带格式化:138 **** ****)。
  • 密码或验证码输入。
  • “获取验证码”按钮(60秒倒计时)。
  • 表单验证(手机号格式、密码长度、验证码6位)。
  • 提交按钮禁用状态控制。
✅ 高级回答要点:

dart

深色版本

class LoginForm extends StatefulWidget {@override_LoginFormState createState() => _LoginFormState();
}class _LoginFormState extends State<LoginForm> {final _formKey = GlobalKey<FormState>();final _phoneController = TextEditingController();final _codeController = TextEditingController();bool _isCodeLogin = true;bool _isSubmitting = false;int _countdown = 0;@overridevoid initState() {super.initState();_phoneController.addListener(_formatPhone);}void _formatPhone() {// 实现手机号格式化:138****1234}void _startCountdown() {setState(() => _countdown = 60);Timer.periodic(const Duration(seconds: 1), (timer) {if (_countdown <= 1) {timer.cancel();setState(() => _countdown = 0);} else {setState(() => _countdown--);}});}void _submit() async {if (_formKey.currentState?.validate() ?? false) {setState(() => _isSubmitting = true);try {await login(_phoneController.text, _codeController.text);Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => Home()));} catch (e) {ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('登录失败: $e')));} finally {setState(() => _isSubmitting = false);}}}@overrideWidget build(BuildContext context) {return Form(key: _formKey,child: Column(children: [TextFormField(controller: _phoneController,decoration: const InputDecoration(labelText: '手机号'),validator: (value) => isValidPhone(value) ? null : '请输入正确的手机号',),if (_isCodeLogin) ...[TextFormField(controller: _codeController,decoration: InputDecoration(labelText: '验证码',suffixIcon: _countdown > 0? Text('$_countdowns'): TextButton(onPressed: _countdown == 0 ? _startCountdown : null,child: const Text('获取验证码'),),),validator: (value) => value?.length == 6 ? null : '验证码为6位',),] else ...[// 密码输入框],ElevatedButton(onPressed: (_formKey.currentState?.isValid ?? false) && !_isSubmitting ? _submit : null,child: _isSubmitting ? const CircularProgressIndicator() : const Text('登录'),),],),);}
}
💡 考察点:
  1. 表单管理GlobalKey<FormState> + TextFormField
  2. 输入控制TextEditingController 监听和格式化。
  3. 状态反馈:倒计时、提交中 loading 状态。
  4. 用户体验:按钮禁用、错误提示。
  5. 可扩展性:支持密码/验证码切换。

🌟 场景题 3:实现一个“聊天界面”,支持文本、图片、语音消息

需求

  • 消息列表(左:别人,右:自己)。
  • 支持文本、图片、语音消息。
  • 图片可点击放大。
  • 语音消息可播放/暂停。
  • 输入框支持文字输入和发送。
✅ 高级回答要点:

dart

深色版本

// 1. 定义消息类型
sealed class ChatMessage {final String sender;final DateTime timestamp;
}class TextMessage extends ChatMessage {final String text;TextMessage({required this.text, required super.sender, required super.timestamp});
}class ImageMessage extends ChatMessage {final String imageUrl;ImageMessage({required this.imageUrl, required super.sender, required super.timestamp});
}class VoiceMessage extends ChatMessage {final String audioUrl;final int duration;VoiceMessage({required this.audioUrl, required this.duration, required super.sender, required super.timestamp});
}// 2. 消息项 Widget
Widget buildMessage(ChatMessage message) {final isMe = message.sender == 'me';return Row(mainAxisAlignment: isMe ? MainAxisAlignment.end : MainAxisAlignment.start,children: [Container(padding: const EdgeInsets.all(8),margin: const EdgeInsets.symmetric(vertical: 4),decoration: BoxDecoration(color: isMe ? Colors.blue : Colors.grey[300],borderRadius: BorderRadius.circular(8),),child: _buildMessageContent(message),),],);
}Widget _buildMessageContent(ChatMessage message) {if (message is TextMessage) {return Text(message.text, style: TextStyle(color: message.sender == 'me' ? Colors.white : Colors.black));} else if (message is ImageMessage) {return GestureDetector(onTap: () => showImageDialog(message.imageUrl),child: Image.network(message.imageUrl, width: 150),);} else if (message is VoiceMessage) {return VoicePlayer(audioUrl: message.audioUrl, duration: message.duration);}return const SizedBox();
}
💡 考察点:
  1. 数据建模:使用 sealed class(Dart 3.0)或 enum + factory 区分消息类型。
  2. UI 复用_buildMessageContent 分类型渲染。
  3. 交互:图片点击放大(showDialog + PhotoView)。
  4. 第三方库audioplayers 播放语音。
  5. 性能:图片懒加载、缓存。

🌟 场景题 4:实现一个“商品详情页”,包含轮播图、标题、价格、规格选择、购买按钮

需求

  • 顶部轮播图(支持自动播放、指示器)。
  • 商品标题、价格、销量。
  • 规格选择(颜色、尺寸),选择后更新价格。
  • “加入购物车”和“立即购买”按钮。
  • 页面滑动时,标题栏颜色渐变。
✅ 高级回答要点:

dart

深色版本

class ProductDetailPage extends StatefulWidget {@override_ProductDetailPageState createState() => _ProductDetailPageState();
}class _ProductDetailPageState extends State<ProductDetailPage> {final ScrollController _scrollController = ScrollController();double _appBarOpacity = 0;@overridevoid initState() {_scrollController.addListener(() {final offset = _scrollController.offset;setState(() {_appBarOpacity = offset.clamp(0.0, 1.0);});});super.initState();}@overrideWidget build(BuildContext context) {return Scaffold(body: Stack(children: [CustomScrollView(controller: _scrollController,slivers: [SliverAppBar(pinned: true,backgroundColor: Colors.white.withOpacity(_appBarOpacity),title: Text('商品详情', style: TextStyle(color: _appBarOpacity > 0.5 ? Colors.black : Colors.white)),),SliverToBoxAdapter(child: Column(children: [CarouselSlider(...), // 轮播图ProductInfo(),     // 标题、价格SpecSelector(),    // 规格选择BuyButtons(),      // 购买按钮],),),],),],),);}
}
💡 考察点:
  1. 复杂布局CustomScrollView + SliverAppBar 实现渐变标题。
  2. 状态联动:规格选择 → 价格更新(Provider 或 Bloc)。
  3. 第三方库carousel_slider 实现轮播。
  4. 用户体验:平滑滚动、视觉反馈。

🌟 场景题 5:实现一个“后台定位服务”(Android/iOS)

需求

  • 应用在后台时持续获取用户位置。
  • 位置变化超过 100 米上报一次。
  • 低功耗模式(不影响电池)。
  • 前台服务显示通知(Android)。
✅ 高级回答要点:
  1. 插件选择geolocator + workmanager(定时任务)或 flutter_background_service
  2. 权限
    • Android: ACCESS_FINE_LOCATIONACCESS_BACKGROUND_LOCATION
    • iOS: NSLocationAlwaysAndWhenInUseUsageDescription
  3. 实现
    dart

    深色版本

    final service = FlutterBackgroundService();
    service.startForeground(onStart: (service) {service.on('location').listen((data) {Geolocator.getPositionStream(distanceFilter: 100, // 100米变化desiredAccuracy: LocationAccuracy.low,).listen((position) {// 上报位置sendToServer(position);});});},
    );
  4. 保活:Android 前台服务 + 通知,iOS BGTaskScheduler
💡 考察点:
  1. 平台特性:了解 Android/iOS 后台限制。
  2. 插件能力:选择合适的插件。
  3. 功耗优化:低精度、距离过滤。
  4. 合规性:隐私权限、用户提示。

总结:如何应对 Flutter 场景题?

步骤说明
1. 理解需求问清楚边界条件、异常情况
2. 设计数据模型定义 classenumstate
3. 选择状态管理ProviderBlocRiverpod
4. 构建 UI 结构Widget 拆分、复用
5. 处理交互手势、动画、表单
6. 考虑性能constListView.builderIsolate
7. 异常与加载loadingerrorretry
8. 内存与生命周期dispose()controller 释放

记住:面试官想看到的是你的思考过程,边说边写,展示架构思维和工程素养。

http://www.xdnf.cn/news/19921.html

相关文章:

  • 数据结构:双向链表
  • 题解:UVA1589 象棋 Xiangqi
  • 基于 CC-Link IE FB 转 DeviceNet 技术的三菱 PLC 与发那科机器人在汽车涂装线的精准喷涂联动
  • Augmentcode免费额度AI开发WordPress商城实战
  • 【全面指南】Claude Code 从入门到精通:安装、配置、命令与高级技巧详解
  • 一个线程池的工作线程run函数的解析
  • Docker 学习笔记
  • 52DH Pro网址导航系统开源版
  • 泰酷辣!我的头像被「移乐AI头像」‘爆改’成顶流了!免费快来薅!
  • 【FastDDS】Layer DDS之Domain (01-overview)
  • 深度学习之第六课卷积神经网络 (CNN)如何保存和使用最优模型
  • 因果机器学习热度攀升,成顶会顶刊 “加分项”,想发论文就认准它!
  • 苍穹外卖项目实战(日记十四)-记录实战教程及问题的解决方法-(day3课后作业) 菜品停售启售功能
  • 机器视觉中为什么优先选择黑白相机?
  • 【Linux】为什么死循环卡不死 Linux?3 个核心逻辑看懂进程优先级与 CPU 调度密码
  • 性能测试-jmeter9-直连数据库
  • 苍穹外卖项目笔记day03
  • 从0 死磕全栈第3天:React Router (Vite + React + TS 版):构建小时站实战指南
  • 机器学习-逻辑回归
  • raspberry Pi 4B(树莓派4B)开启VNC服务 主机用VNC连接
  • 14、Docker构建后端镜像并运行
  • 关于SPI串口spidev接收数据不完整的问题
  • Moonchain:「新加坡大华银行」加持下连接现实金融与链上经济的价值通道
  • 大数据毕业设计选题推荐-基于大数据的电信客户流失数据分析系统-Hadoop-Spark-数据可视化-BigData
  • 03、Maven下载与阿里云镜像加速
  • 电子电气架构 --- 新EEA架构下开发模式转变
  • Openmanus复现教程:打造自己的Agent助手
  • Python之split - 常遇见的bug
  • Redis突然挂了,数据丢了多少?就看你用RDB还是AOF
  • Git配置:禁用全局HTTPS验证