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

Flutter集成ASP.NET API:如何将Model转换为List实现搜索

解决Flutter与ASP.NET API集成中的搜索功能问题

首先明确核心逻辑:你的listmodel本身已经包含了要展示的数据列表rows(类型为List<Rows>),不需要把整个listmodel转换为List,搜索功能直接基于rows列表实现即可。下面针对你的代码问题给出具体修复方案:

一、修正当前代码的错误点

你在BlocBuilder中的写法存在本质错误:

  1. state.listModel.toJson()会将模型转为Map,而非List,后续List.from(readmodel as Iterable)完全不成立
  2. readmodel.entries((element) => element.drainageid)语法错误,Map的entries返回的是键值对,不存在drainageid属性

二、方案一:局部状态实现前端过滤(简单快速)

如果搜索仅在当前页面生效,无需全局状态同步,直接用StatefulWidget的局部状态维护过滤后的数据:

1. 在State类中定义变量

class _YourPageState extends State<YourPage> {
  late List<Rows> _originalRows; // 保存原始数据,用于重置过滤
  late List<Rows> _filteredRows; // 过滤后的数据,用于UI展示

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<ListBloc, ListState>(
      builder: (context, state) {
        if (state is ListInitial || state is ListLoading) {
          return _buildLoading();
        } else if (state is ListLoaded) {
          // 初始化原始数据和过滤列表
          _originalRows = state.listModel.rows ?? [];
          _filteredRows = List.from(_originalRows);

          // 搜索过滤方法
          void updateFilter(String value) {
            setState(() {
              _filteredRows = _originalRows.where((row) {
                // 自定义搜索规则:匹配ID或检查人字段,忽略大小写
                final idMatch = row.id?.toLowerCase().contains(value.toLowerCase()) ?? false;
                final inspMatch = row.inspby?.toLowerCase().contains(value.toLowerCase()) ?? false;
                return idMatch || inspMatch;
              }).toList();
            });
          }

          return Column(
            children: [
              // 搜索输入框
              TextField(
                onChanged: updateFilter,
                decoration: const InputDecoration(
                  hintText: "搜索ID/检查人",
                  prefixIcon: Icon(Icons.search),
                ),
              ),
              // 过滤后的列表
              Expanded(child: _buildListView(context, _filteredRows)),
            ],
          );
        } else if (state is ListError) {
          return const Center(child: Text("加载失败"));
        } else {
          return const SizedBox();
        }
      },
    );
  }

  // 修改列表构建方法,接收Rows列表
  Widget _buildListView(BuildContext context, List<Rows> rows) {
    return ListView.builder(
      itemCount: rows.length,
      itemBuilder: (context, index) {
        final row = rows[index];
        return ListTile(
          title: Text(row.id ?? "无ID"),
          subtitle: Text(row.inspby ?? "无检查人"),
          trailing: Text(row.dateofinsp?.toString().split(" ").first ?? "无日期"),
        );
      },
    );
  }
}

三、方案二:Bloc全局状态管理搜索

如果需要多页面共享搜索状态,或者需要配合后端搜索,用Bloc事件驱动实现:

1. 添加搜索事件

abstract class ListEvent {}

class FetchListEvent extends ListEvent {}

class SearchListEvent extends ListEvent {
  final String query;
  SearchListEvent(this.query);
}

2. 修改Bloc状态结构

abstract class ListState {}

class ListInitial extends ListState {}

class ListLoading extends ListState {}

class ListLoaded extends ListState {
  final ListModel listModel;
  final List<Rows>? filteredRows;

  ListLoaded(this.listModel, {this.filteredRows});
}

class ListError extends ListState {
  final String error;
  ListError(this.error);
}

3. Bloc处理搜索逻辑

class ListBloc extends Bloc<ListEvent, ListState> {
  final ApiRepository repository;
  late ListModel _originalModel;

  ListBloc(this.repository) : super(ListInitial()) {
    on<FetchListEvent>(_fetchList);
    on<SearchListEvent>(_handleSearch);
  }

  void _fetchList(FetchListEvent event, Emitter<ListState> emit) async {
    emit(ListLoading());
    try {
      final model = await repository.fetchlistList();
      _originalModel = model;
      emit(ListLoaded(model, filteredRows: model.rows));
    } catch (e) {
      emit(ListError(e.toString()));
    }
  }

  // 前端过滤逻辑
  void _handleSearch(SearchListEvent event, Emitter<ListState> emit) {
    final rows = _originalModel.rows ?? [];
    final filtered = rows.where((row) {
      final idMatch = row.id?.toLowerCase().contains(event.query.toLowerCase()) ?? false;
      final inspMatch = row.inspby?.toLowerCase().contains(event.query.toLowerCase()) ?? false;
      return idMatch || inspMatch;
    }).toList();
    emit(ListLoaded(_originalModel, filteredRows: filtered));
  }
}

4. BlocBuilder中使用过滤后的数据

BlocBuilder<ListBloc, ListState>(
  builder: (context, state) {
    if (state is ListInitial || state is ListLoading) {
      return _buildLoading();
    } else if (state is ListLoaded) {
      return Column(
        children: [
          TextField(
            onChanged: (value) => context.read<ListBloc>().add(SearchListEvent(value)),
            decoration: const InputDecoration(hintText: "搜索ID/检查人"),
          ),
          Expanded(child: _buildListView(context, state.filteredRows ?? [])),
        ],
      );
    } else {
      return const Center(child: Text("加载失败"));
    }
  },
)

四、后端搜索优化(可选)

如果数据量较大,前端过滤性能差,可以让ASP.NET API支持搜索参数,修改API请求逻辑:

1. 修改ApiProvider请求方法

Future<ListModel> fetchlistList({String? searchQuery}) async {
  try {
    final params = searchQuery != null ? {"search": searchQuery} : {};
    final response = await _dio.get(
      _url,
      options: Options(headers: {"keyishere": "xxxxxxx"}),
      queryParameters: params,
    );
    return ListModel.fromJson(response.data);
  } catch (error, stacktrace) {
    print("Exception: $error StackTrace: $stacktrace");
    return ListModel.withError("数据加载失败/连接问题");
  }
}

2. 修改Bloc的搜索事件逻辑

void _handleSearch(SearchListEvent event, Emitter<ListState> emit) async {
  emit(ListLoading());
  try {
    final model = await repository.fetchlistList(searchQuery: event.query);
    emit(ListLoaded(model, filteredRows: model.rows));
  } catch (e) {
    emit(ListError(e.toString()));
  }
}

额外规范建议

Dart命名规范要求类名首字母大写,建议修改:

  • listmodel → ListModel
  • listState → ListState
  • Rows → InspectionRow(避免和Flutter内置Row组件冲突)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 18:57:58