简体   繁体   English

如何对Flutter中的JSON数据进行文本搜索?

[英]How to perform a text search over JSON data in Flutter?

I am working on an application which requires to fetch some JSON data from firebase storage and then a user can perform a search over it.我正在开发一个应用程序,它需要从 firebase 存储中获取一些 JSON 数据,然后用户可以对其进行搜索。 But, my search query isn't working properly and I get one error when I perform the search multiple times.但是,我的搜索查询无法正常工作,并且在我多次执行搜索时出现一个错误。

I have first uploaded the data to firebase storage and then fetched it.我先把数据上传到firebase存储,然后去取。 The search is working sometimes only.搜索有时只在工作。 I cannot figure out what is wrong with the code.我无法弄清楚代码有什么问题。

Here is the complete code这是完整的代码

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'dart:convert';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Contacts Demo',
      debugShowCheckedModeBanner: false,
      home: StorageUpload(),
    );
  }
}

class StorageUpload extends StatefulWidget {
  @override
  StorageUploadState createState() => new StorageUploadState();
}

class StorageUploadState extends State<StorageUpload> {
  var rows = [];
  String query = '';
  TextEditingController tc;

  @override
  void initState() {
    super.initState();
    tc = TextEditingController();
    rows = [
      {
        'contact_name': 'Test User 1',
        'contact_phone': '066 560 4900',
      },
      {
        'contact_name': 'Test User 2',
        'contact_phone': '066 560 7865',
      },
      {
        'contact_name': 'Test User 3',
        'contact_phone': '906 500 4334',
      }
    ];
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(
          "Delivero Contacts",
          style: new TextStyle(
            color: Colors.white,
          ),
        ),
      ),
      body: Container(
        color: Colors.white,
        padding: EdgeInsets.all(10),
        child: Stack(
          children: [
            Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: TextField(
                    controller: tc,
                    decoration: InputDecoration(hintText: 'Search...'),
                    onChanged: (v) {
                      setState(() {
                        query = v;
                      });
                    },
                  ),
                ),
                Container(
                  color: Colors.white,
                  child: ListView.builder(
                    shrinkWrap: true,
                    itemCount: rows.length,
                    itemBuilder: (con, ind) {
                      return query.isEmpty
                          ? ListTile(
                              title: Text(rows[ind]['contact_name']),
                              subtitle: Text(rows[ind]['contact_phone']),
                              onTap: () {
                                setState(() {
                                  tc.text = rows[ind]['contact_name'];
                                  query = rows[ind]['contact_name'];
                                });
                              },
                            )
                          : rows[ind]['contact_name']
                                      .toString()
                                      .toLowerCase()
                                      .contains(query.toLowerCase()) ||
                                  rows[ind]['contact_phone']
                                      .toString()
                                      .toLowerCase()
                                      .contains(query.toLowerCase())
                              ? ListTile(
                                  title: Text(rows[ind]['contact_name']),
                                  subtitle: Text(rows[ind]['contact_phone']),
                                  onTap: () {
                                    setState(() {
                                      tc.text = rows[ind]['contact_name'];
                                      query = rows[ind]['contact_name'];
                                    });
                                  },
                                )
                              : null;
                    },
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Here is the error I am getting这是我得到的错误


════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during performLayout():
'package:flutter/src/rendering/sliver_multi_box_adaptor.dart': Failed assertion: line 662 pos 16: 'indexOf(child) == index': is not true.


Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md

The relevant error-causing widget was
    ListView 
lib/main.dart:104
When the exception was thrown, this was the stack
#2      RenderSliverMultiBoxAdaptor.debugAssertChildListIsNonEmptyAndContiguous.<anonymous closure> 
package:flutter/…/rendering/sliver_multi_box_adaptor.dart:662
#3      RenderSliverMultiBoxAdaptor.debugAssertChildListIsNonEmptyAndContiguous 
package:flutter/…/rendering/sliver_multi_box_adaptor.dart:666
#4      RenderSliverList.performLayout 
package:flutter/…/rendering/sliver_list.dart:282
#5      RenderObject.layout 
package:flutter/…/rendering/object.dart:1767
#6      RenderSliverEdgeInsetsPadding.performLayout 
package:flutter/…/rendering/sliver_padding.dart:135
...
The following RenderObject was being processed when the exception was fired: RenderSliverList#5c228 relayoutBoundary=up17 NEEDS-LAYOUT NEEDS-COMPOSITING-BITS-UPDATE
RenderObject: RenderSliverList#5c228 relayoutBoundary=up17 NEEDS-LAYOUT NEEDS-COMPOSITING-BITS-UPDATE
    needs compositing
    parentData: paintOffset=Offset(0.0, 0.0) (can use size)
    constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle, scrollOffset: 0.0, remainingPaintExtent: Infinity, crossAxisExtent: 372.7, crossAxisDirection: AxisDirection.right, viewportMainAxisExtent: Infinity, remainingCacheExtent: Infinity, cacheOrigin: 0.0)
    geometry: SliverGeometry(scrollExtent: 224.0, paintExtent: 224.0, maxPaintExtent: 224.0, cacheExtent: 224.0)
        scrollExtent: 224.0
        paintExtent: 224.0
        maxPaintExtent: 224.0
        cacheExtent: 224.0
    currently live children: 0 to 3
    child with index 0: RenderIndexedSemantics#ea1e2 relayoutBoundary=up18
        needs compositing
        parentData: index=0; layoutOffset=0.0 (can use size)
        constraints: BoxConstraints(w=372.7, 0.0<=h<=Infinity)
        semantic boundary
        size: Size(372.7, 56.0)
        index: 0
        child: RenderRepaintBoundary#17c6b relayoutBoundary=up19
            needs compositing
            parentData: <none> (can use size)
            constraints: BoxConstraints(w=372.7, 0.0<=h<=Infinity)
            layer: OffsetLayer#34eb7
                engine layer: OffsetEngineLayer#a8a79
                offset: Offset(0.0, 0.0)
            size: Size(372.7, 56.0)
            metrics: 0.0% useful (1 bad vs 0 good)
            diagnosis: insufficient data to draw conclusion (less than five repaints)
            child: RenderSemanticsAnnotations#2ecd5 relayoutBoundary=up20
                parentData: <none> (can use size)
                constraints: BoxConstraints(w=372.7, 0.0<=h<=Infinity)
                size: Size(372.7, 56.0)
                child: RenderMouseRegion#9a9a8 relayoutBoundary=up21
                    parentData: <none> (can use size)
                    constraints: BoxConstraints(w=372.7, 0.0<=h<=Infinity)
                    size: Size(372.7, 56.0)
                    listeners: enter, exit
                    cursor: SystemMouseCursor(click)
    child with index 3: RenderIndexedSemantics#96641 relayoutBoundary=up18
        needs compositing
        parentData: index=3; layoutOffset=168.0 (can use size)
        constraints: BoxConstraints(w=372.7, 0.0<=h<=Infinity)
        semantic boundary
        size: Size(372.7, 56.0)
        index: 3
        child: RenderRepaintBoundary#13951 relayoutBoundary=up19
            needs compositing
            parentData: <none> (can use size)
            constraints: BoxConstraints(w=372.7, 0.0<=h<=Infinity)
            layer: OffsetLayer#768be
                engine layer: OffsetEngineLayer#51b91
                offset: Offset(0.0, 168.0)
            size: Size(372.7, 56.0)
            metrics: 0.0% useful (1 bad vs 0 good)
            diagnosis: insufficient data to draw conclusion (less than five repaints)
            child: RenderSemanticsAnnotations#77b2c relayoutBoundary=up20
                parentData: <none> (can use size)
                constraints: BoxConstraints(w=372.7, 0.0<=h<=Infinity)
                size: Size(372.7, 56.0)
                child: RenderMouseRegion#9ae66 relayoutBoundary=up21
                    parentData: <none> (can use size)
                    constraints: BoxConstraints(w=372.7, 0.0<=h<=Infinity)
                    size: Size(372.7, 56.0)
                    listeners: enter, exit
                    cursor: SystemMouseCursor(click)
════════════════════════════════════════════════════════════════════════════════

I am a Flutter noob.我是 Flutter 菜鸟。 How can I perform a full-text search over different JSON field from the data fetched from firebase storage?如何根据从 firebase 存储中获取的数据对不同的 JSON 字段执行全文搜索? Any help would be amazing.任何帮助都会很棒。 Thanks for your time!谢谢你的时间!

import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Contacts Demo',
      debugShowCheckedModeBanner: false,
      home: StorageUpload(),
    );
  }
}

class StorageUpload extends StatefulWidget {
  @override
  StorageUploadState createState() => new StorageUploadState();
}

class StorageUploadState extends State<StorageUpload> {
  List<Map<String,dynamic>> original=[],duplicate=[];
  String query = '';
  TextEditingController tc;
  StreamController<List<Map<String,dynamic>>> controller = StreamController<List<Map<String,dynamic>>>();

  @override
  void initState() {
    super.initState();
    tc = TextEditingController();
    original = [
      {
        'contact_name': 'Test User 1',
        'contact_phone': '066 560 4900',
      },
      {
        'contact_name': 'Test User 2',
        'contact_phone': '066 560 7865',
      },
      {
        'contact_name': 'Test User 3',
        'contact_phone': '906 500 4334',
      }
    ];
    duplicate.addAll(original);
    controller.sink.add(duplicate);
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(
          "Delivero Contacts",
          style: new TextStyle(
            color: Colors.white,
          ),
        ),
      ),
      body: Container(
        color: Colors.white,
        padding: EdgeInsets.all(10),
        child: Stack(
          children: [
            Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: TextField(
                    controller: tc,
                    decoration: InputDecoration(hintText: 'Search...'),
                    onChanged: (v) {
                      if(v.isEmpty){
                        duplicate = original;
                        controller.sink.add(duplicate);
                        return;
                      }

                      //If your data is a Map object you can use toString()
                      //and avoid checking both fields
                      duplicate = original.where((m)=>
                                                 m['contact_name'].toString().toLowerCase().contains(v.toLowerCase())||
                                                m['contact_phone'].toString().toLowerCase().contains(v.toLowerCase())).toList();
                      controller.sink.add(duplicate);
                    },
                  ),
                ),
                Container(
                  color: Colors.white,
                  child: StreamBuilder<Object>(
                    stream: controller.stream,
                    builder: (context, snapshot) {
                      return ListView.builder(
                        shrinkWrap: true,
                        itemCount: duplicate.length,
                        itemBuilder: (con, ind) {
                          return query.isEmpty
                              ? ListTile(
                                  title: Text(duplicate[ind]['contact_name']),
                                  subtitle: Text(duplicate[ind]['contact_phone']),
                                  onTap: () {
                                    setState(() {
                                      tc.text = duplicate[ind]['contact_name'];
                                      query = duplicate[ind]['contact_name'];
                                    });
                                  },
                                )
                              : duplicate[ind]['contact_name']
                                          .toString()
                                          .toLowerCase()
                                          .contains(query) ||
                                      duplicate[ind]['contact_phone']
                                          .toString()
                                          .toLowerCase()
                                          .contains(query)
                                  ? ListTile(
                                      title: Text(duplicate[ind]['contact_name']),
                                      subtitle: Text(duplicate[ind]['contact_phone']),
                                      onTap: () {
                                        setState(() {
                                          tc.text = duplicate[ind]['contact_name'];
                                          query = duplicate[ind]['contact_name'];
                                        });
                                      },
                                    ):null;
                        },
                      );
                    }
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
  @override
  void dispose(){
    controller?.close();
    super.dispose();
  }
}

I slightly changed your code to make a working sample.我稍微更改了您的代码以制作一个工作示例。 I added a results List object and I moved the "query.isEmpty" check before the ListView:我添加了一个结果列表 object 并且我在 ListView 之前移动了“query.isEmpty”检查:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Contacts Demo',
      debugShowCheckedModeBanner: false,
      home: StorageUpload(),
    );
  }
}

class StorageUpload extends StatefulWidget {
  @override
  StorageUploadState createState() => new StorageUploadState();
}

class StorageUploadState extends State<StorageUpload> {
  List results = [];

  var rows = [];
  String query = '';
  TextEditingController tc;

  @override
  void initState() {
    super.initState();
    tc = TextEditingController();
    rows = [
      {
        'contact_name': 'Test User 1',
        'contact_phone': '066 560 4900',
      },
      {
        'contact_name': 'Test User 2',
        'contact_phone': '066 560 7865',
      },
      {
        'contact_name': 'Test User 3',
        'contact_phone': '906 500 4334',
      }
    ];
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(
          "Delivero Contacts",
          style: new TextStyle(
            color: Colors.white,
          ),
        ),
      ),
      body: Container(
        color: Colors.white,
        padding: EdgeInsets.all(10),
        child: Stack(
          children: [
            Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: TextField(
                    controller: tc,
                    decoration: InputDecoration(hintText: 'Search...'),
                    onChanged: (v) {
                      setState(() {
                        query = v;
                        setResults(query);
                      });
                    },
                  ),
                ),
                Container(
                  color: Colors.white,
                  child: query.isEmpty
                      ? ListView.builder(
                          shrinkWrap: true,
                          itemCount: rows.length,
                          itemBuilder: (con, ind) {
                            return ListTile(
                              title: Text(rows[ind]['contact_name']),
                              subtitle: Text(rows[ind]['contact_phone']),
                              onTap: () {
                                setState(() {
                                  tc.text = rows[ind]['contact_name'];
                                  query = rows[ind]['contact_name'];
                                  setResults(query);
                                });
                              },
                            );
                          },
                        )
                      : ListView.builder(
                          shrinkWrap: true,
                          itemCount: results.length,
                          itemBuilder: (con, ind) {
                            return ListTile(
                              title: Text(results[ind]['contact_name']),
                              subtitle: Text(results[ind]['contact_phone']),
                              onTap: () {
                                setState(() {
                                  tc.text = results[ind]['contact_name'];
                                  query = results[ind]['contact_name'];
                                  setResults(query);
                                });
                              },
                            );
                          },
                        ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  void setResults(String query) {
    results = rows
        .where((elem) =>
            elem['contact_name']
                .toString()
                .toLowerCase()
                .contains(query.toLowerCase()) ||
            elem['contact_phone']
                .toString()
                .toLowerCase()
                .contains(query.toLowerCase()))
        .toList();
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM