我想使用 flutter 中的复杂 JSON 数据构建一个动态 Tab Bar 和相应的页面,其中包含与该选项卡对应的项目列表

问题描述

我正在尝试使用 Flutter 构建一个动态标签栏,该栏显示带有与我的 JSON 数据对应的标签名称标签,然后对于每个键,我想将它的值映射为相应标签中的 ListView。从本地资产文件中检索 JSON 数据。我尝试了多种方法,但仍然出现错误。此外,ListView 的代码正在运行,我只是无法弄清楚如何使用 JSON 映射它。任何帮助将不胜感激。

I'm sharing a reference of what I'm trying to build

这是我最后一次尝试的代码

JSON 文件

[
    {
        "vegetables": [
            {
                "name": "Potato","minorder": 1.5,"pricePerKg": 12.5,"imageUrl": "assets/images/potato.jpg","description": "Very Healthy but high fat"
            },{
                "name": "Cabbage","minorder": 1,"pricePerKg": 10,"imageUrl": "assets/images/cabbage.jpg","description": "Very Healthy but be aware"
            },{
                "name": "Lady Finger","imageUrl": "assets/images/lady_finger.jpg","description": "Very Healthy green vegetable healthy for eyesight"
            },{
                "name": "Spinach","imageUrl": "assets/images/spinach.jpg","description": "Very Healthy green leafy"
            },{
                "name": "Cauliflower","imageUrl": "assets/images/cauliflower.jpg","description": "Very Healthy keeps doctor away"
            },{
                "name": "Capsicum","imageUrl": "assets/images/capsicum.jpg","description": "Very Healthy and green vegetable"
            }
        ]
    },{
        "Fruits": [
            {
                "name": "Banana","imageUrl": "assets/images/banana.jpg","description": "Very Healthy helps in gaining weight"
            },{
                "name": "Orange","imageUrl": "assets/images/orange.jpg","description": "Very Healthy and citrus"
            },{
                "name": "Apple","imageUrl": "assets/images/apple.jpg","description": "Very Healthy keeps doctor away"
            }
        ]
    },{
        "Bakery": [
            {
                "name": "Cake",{
                "name": "Cookies",{
        "Fish": [
            {
                "name": "Gold Fish",{
                "name": "Luna",{
        "Poultry": [
            {
                "name": "White Egg",{
                "name": "brown Egg",{
                "name": "Chicken meat","description": "Very Healthy keeps doctor away"
            }
        ]
    }
]

Dart 代码

import 'dart:convert';

import 'package:asset_app/navigation/custom_bottom_nav_bar.dart';
import 'package:Flutter/material.dart';

class ParsingJson extends StatefulWidget {
  const ParsingJson({Key key}) : super(key: key);

  @override
  _ParsingJsonState createState() => _ParsingJsonState();
}

class _ParsingJsonState extends State<ParsingJson> {
  Future<String> jsonString;
  List _cards;
  Map _data;
  int _initPosition = 1;

  @override
  void initState() {
    super.initState();
    jsonString = DefaultAssetBundle.of(context)
        .loadString("assets/json_file/categories.json");
    // .then((d) {
    // _cards = json.decode(d);
    // setState(() => _data = _cards[0]);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("JSON parsing demo"),),body: SafeArea(
            child: FutureBuilder(
                future: jsonString,builder: (context,snapshot) {
                  if (snapshot.hasData) {
                    // Decode the JSON
                    Map newData = json.decode(snapshot.data.toString())[0];
                    return CustomTabView(
                      initPosition: _initPosition,itemCount: (newData.keys as List).length + 1,tabBuilder: (context,index) =>
                          Tab(text: (newData.keys.toList())[index]),pageBuilder: (context,index) =>
                          Center(child: Text("data[index]")),onPositionChange: (index) {
                        print('current position: $index');
                        _initPosition = index;
                      },onScroll: (position) => print('$position'),);
                  }
                  return CircularProgressIndicator();
                })));
  }
}

标签条码

import 'package:Flutter/material.dart';

class CustomTabView extends StatefulWidget {
  final int itemCount;
  final IndexedWidgetBuilder tabBuilder;
  final IndexedWidgetBuilder pageBuilder;
  final Widget stub;
  final ValueChanged<int> onPositionChange;
  final ValueChanged<double> onScroll;
  final int initPosition;

  CustomTabView({
    @required this.itemCount,@required this.tabBuilder,@required this.pageBuilder,this.stub,this.onPositionChange,this.onScroll,this.initPosition,});

  @override
  _CustomTabsstate createState() => _CustomTabsstate();
}

class _CustomTabsstate extends State<CustomTabView>
    with TickerProviderStateMixin {
  TabController controller;
  int _currentCount;
  int _currentPosition;

  @override
  void initState() {
    _currentPosition = widget.initPosition ?? 0;
    controller = TabController(
      length: widget.itemCount,vsync: this,initialIndex: _currentPosition,);
    controller.addListener(onPositionChange);
    controller.animation.addListener(onScroll);
    _currentCount = widget.itemCount;
    super.initState();
  }

  @override
  void didUpdateWidget(CustomTabView oldWidget) {
    if (_currentCount != widget.itemCount) {
      controller.animation.removeListener(onScroll);
      controller.removeListener(onPositionChange);
      controller.dispose();

      if (widget.initPosition != null) {
        _currentPosition = widget.initPosition;
      }

      if (_currentPosition > widget.itemCount - 1) {
        _currentPosition = widget.itemCount - 1;
        _currentPosition = _currentPosition < 0 ? 0 : _currentPosition;
        if (widget.onPositionChange is ValueChanged<int>) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) {
              widget.onPositionChange(_currentPosition);
            }
          });
        }
      }

      _currentCount = widget.itemCount;
      setState(() {
        controller = TabController(
          length: widget.itemCount,);
        controller.addListener(onPositionChange);
        controller.animation.addListener(onScroll);
      });
    } else if (widget.initPosition != null) {
      controller.animateto(widget.initPosition);
    }

    super.didUpdateWidget(oldWidget);
  }

  @override
  void dispose() {
    controller.animation.removeListener(onScroll);
    controller.removeListener(onPositionChange);
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (widget.itemCount < 1) return widget.stub ?? Container();

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,children: <Widget>[
        Container(
          alignment: Alignment.center,child: TabBar(
            isScrollable: true,controller: controller,labelColor: Theme.of(context).primaryColor,unselectedLabelColor: Theme.of(context).hintColor,indicator: Boxdecoration(
              border: Border(
                bottom: BorderSide(
                  color: Theme.of(context).primaryColor,width: 2,tabs: List.generate(
              widget.itemCount,(index) => widget.tabBuilder(context,index),Expanded(
          child: TabBarView(
            controller: controller,children: List.generate(
              widget.itemCount,(index) => widget.pageBuilder(context,],);
  }

  onPositionChange() {
    if (!controller.indexIsChanging) {
      _currentPosition = controller.index;
      if (widget.onPositionChange is ValueChanged<int>) {
        widget.onPositionChange(_currentPosition);
      }
    }
  }

  onScroll() {
    if (widget.onScroll is ValueChanged<double>) {
      widget.onScroll(controller.animation.value);
    }
  }
}

错误- 构建 FutureBuilder(dirty,state: _FutureBuilderState#6d218) 时抛出了以下 _CastError: 类型 '_CompactIterable' 不是类型转换中类型 'List' 的子类型

解决方法

这里错了:

itemCount: (newData.keys as List).length + 1,

因为 newData.keys_CompactIterable 所以不能强制转换为列表

=> itemCount: newData.keys.toList().length + 1,