You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter代码精简求助:搜索栏逻辑封装函数失败

代码精简与逻辑封装方案

以下是对你的Flutter代码的精简优化,重点将搜索栏、清除按钮及筛选逻辑封装为独立方法,无需重写整体代码:

核心优化点

  • 将搜索栏提取为独立的_buildSearchBar()方法
  • 把清除搜索的逻辑封装到_clearSearch()函数
  • 将列表筛选逻辑抽离为_filterResults(String query)方法
  • 提取列表项构建逻辑到_buildListItem(Prt item, int index)
  • 修复潜在的空安全问题及冗余的setState调用

重构后的完整代码

import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:velneoapp/api/modelos/api_model_partes.dart';
import 'package:velneoapp/views/partes/detalle_de_parte.dart';

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

  @override
  State<PartesView> createState() => _PartesViewState();
}

class Debouncer {
  Timer? timer;

  run(VoidCallback action) {
    timer?.cancel();
    timer = Timer(const Duration(milliseconds: 1000), action);
  }
}

class _PartesViewState extends State<PartesView> {
  String _searchQuery = "";
  final TextEditingController _searchController = TextEditingController();
  Partes? _dataFromAPI;
  final _debouncer = Debouncer();
  bool _isLoading = true;
  List<Prt> _filteredItems = [];

  @override
  void initState() {
    super.initState();
    _fetchData();
  }

  Future<void> _fetchData() async {
    try {
      const url = "https://demoapi.velneo.com/verp-api/vERP_2_dat_dat/v1/vta_ped_g?page%5Bsize%5D=20&fields=id,clt,emp&api_key=api123";
      final res = await http.get(Uri.parse(url));
      
      if (res.statusCode == 200) {
        log("Correcto");
        _dataFromAPI = Partes.fromJson(json.decode(res.body));
        _filteredItems = _dataFromAPI!.vtaPedGs;
      } else {
        log("${res.statusCode}");
        throw Exception("Error durante la conexión");
      }
    } catch (e) {
      log("Error antes de conectarse => ${e.toString()}");
      _filteredItems = [];
    } finally {
      setState(() => _isLoading = false);
    }
  }

  void _clearSearch() {
    _searchController.clear();
    setState(() {
      _searchQuery = "";
      _filteredItems = _dataFromAPI?.vtaPedGs ?? [];
    });
  }

  void _filterResults(String query) {
    setState(() {
      _searchQuery = query;
      if (_dataFromAPI == null) return;
      
      _filteredItems = _dataFromAPI!.vtaPedGs.where((item) => 
        item.id.toString().toLowerCase().contains(query.toLowerCase()) ||
        item.clt.toString().toLowerCase().contains(query.toLowerCase()) ||
        item.emp.toLowerCase().contains(query.toLowerCase())
      ).toList();
    });
    log("valores: $_filteredItems");
  }

  Widget _buildSearchBar() {
    return Container(
      padding: const EdgeInsets.all(15),
      child: TextField(
        textInputAction: TextInputAction.search,
        controller: _searchController,
        decoration: InputDecoration(
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(25.0),
            borderSide: const BorderSide(color: Colors.grey),
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(20.0),
            borderSide: const BorderSide(color: Colors.blue),
          ),
          suffixIcon: IconButton(
            icon: const Icon(Icons.clear),
            onPressed: _clearSearch,
          ),
          contentPadding: const EdgeInsets.all(15.0),
          hintText: 'Buscar...',
        ),
        onChanged: (query) => _debouncer.run(() => _filterResults(query)),
      ),
    );
  }

  Widget _buildListItem(Prt item, int index) {
    return GestureDetector(
      onTap: () {
        setId(_dataFromAPI!.vtaPedGs[index].id);
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => const DetalleDePartesView()),
        );
      },
      child: Card(
        child: ClipPath(
          clipper: ShapeBorderClipper(
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3)),
          ),
          child: Container(
            padding: const EdgeInsets.all(4),
            decoration: const BoxDecoration(
              border: Border(left: BorderSide(color: Colors.green, width: 5)),
            ),
            child: Padding(
              padding: const EdgeInsets.all(5.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text("ID: ${item.id}", style: const TextStyle(fontSize: 16)),
                  Text("Cliente: ${item.clt}", style: const TextStyle(fontSize: 16)),
                  Text("Empresa: ${item.emp}", style: const TextStyle(fontSize: 16)),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Partes")),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : Column(
              children: [
                _buildSearchBar(),
                Expanded(
                  child: ListView.builder(
                    shrinkWrap: true,
                    physics: const ClampingScrollPhysics(),
                    padding: const EdgeInsets.all(5),
                    itemCount: _filteredItems.length,
                    itemBuilder: (context, index) => _buildListItem(_filteredItems[index], index),
                  ),
                ),
              ],
            ),
    );
  }
}

关键优化说明

  1. 搜索栏封装:_buildSearchBar()将原本嵌套在build方法中的搜索栏代码完全独立,使主build方法更简洁,且搜索栏逻辑可复用。
  2. 清除逻辑封装:_clearSearch()统一处理清空输入框、重置搜索词和恢复全量数据的逻辑,避免重复代码。
  3. 筛选逻辑抽离:_filterResults()将列表筛选逻辑单独提取,便于后续修改筛选规则,同时让onChanged回调更简洁。
  4. 列表项构建:_buildListItem()把列表项的UI和交互逻辑封装,减少ListView.builder的代码复杂度。
  5. 空安全与代码规范:
    • 修复了数据请求中可能出现的空指针问题
    • 使用finally块确保加载状态正确更新
    • 变量命名更具语义化(如valores改为_filteredItems)
    • 移除了冗余的setState调用

内容的提问来源于stack exchange,提问作者Quelox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:37:06