Reputation: 463
I want to show unique header from JSON (Ex: "Header 1"; "Header 2"; "Header 3") like this:
this is JSON file:
[
{
"header": "Header 1",
"line": "line 1 of Header 1"
},
{
"header": "Header 1",
"line": "line 2 of Header 1"
},
{
"header": "Header 2",
"line": "line 1 of Header 2"
},
{
"header": "Header 2",
"line": "line 2 of Header 2"
},
{
"header": "Header 3",
"line": "line 1 of Header 3"
},
{
"header": "Header 3",
"line": "line 2 of Header 3"
}
]
so pls help me, I am using the package StickyHeader
but I think that SliverAppBar
can also do it, this is main file:
import 'package:ask/model/header_model.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:sticky_headers/sticky_headers/widget.dart';
class Page1 extends StatefulWidget {
@override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
Future<List<Header>> getHeaderFromJson(BuildContext context) async {
String jsonString = await DefaultAssetBundle.of(context).loadString('assets/header.json');
List<dynamic> raw = jsonDecode(jsonString);
return raw.map((e) => Header.fromJson(e)).toList();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Header')),
body: FutureBuilder(
future: getHeaderFromJson(context),
builder: (context, data) {
List<Header> _header = data.data;
return _header == null
? Container()
: ListView.builder(
itemCount: _header.length,
itemBuilder: (context, index) {
return StickyHeader(
header: Container(
color: Colors.grey,
alignment: Alignment.center,
child: Text(_header[index].header), // I think this line needs to modify
),
content: Column(
children: [Text(_header[index].line)],
),
);
});
}));
}
}
Upvotes: 0
Views: 667
Reputation: 8998
I know that you are confused. But let me tell you one thing, the way you are setting up the widget is wrong, as per your requirements.
If you closely take a look at the data, this is something like this:
[
{
"header": "Header 1",
"line": "line 1 of Header 1"
},
{
"header": "Header 1",
"line": "line 2 of Header 1"
},
{
"header": "Header 2",
"line": "line 1 of Header 2"
},
{
"header": "Header 2",
"line": "line 2 of Header 2"
},
{
"header": "Header 3",
"line": "line 1 of Header 3"
},
{
"header": "Header 3",
"line": "line 2 of Header 3"
}
]
Now if you're looping through the data via your ListView.builder
, the machine will print out one item at a time, hence you are seeing the output on your Device screen like that.
// Just do that and see for yourself, what the outcome is there
// suppose we name your array data as _data
_data.forEach((element){
print(element["header"]);
print(element["list"]);
});
/* The output you will get it like this only
Header 1
line 1 of Header 1
Header 1
line 2 of Header 1
Header 2
line 1 of Header 2
Header 2
line 2 of Header 2
Header 3
line 1 of Header 3
Header 3
line 2 of Header 3
*/
And this is how you are getting on your screen, isn't it? The machine is doing the job in this way.
SOLUTION: What is required for you to do is to organize the item, such that your "header"
should contain all the "line"
associated to the right header
only. You can do it via creating your Map
and store the data like this:
Map _newData = {};
// basic idea is, if the header is same, the value has all the lines
// aligned to it
_data.forEach((element){
if(_newData.containsKey(element["header"])){
_newData[element["header"]].add(element["line"]);
}else{
_newData[element["header"]] = [element["line"]];
}
});
print(_newData);
//OUTPUT will come like this
// {Header 1: [line 1 of Header 1, line 2 of Header 1], Header 2: [line 1 of Header 2, line 2 of Header 2], Header 3: [line 1 of Header 3, line 2 of Header 3]}
Can you the data now, Header 1
has all the line
associated to it, and so on.
So, now how we can achieve this in our app. I have not used any StickyHeader
or something, with simple Flutter widgets, I have recreated your UI. See, and hope you can learn something new
class _MyHomePageState extends State<MyHomePage> {
// Used the data directly, since I don't have the API data
List<Map> _data = [
{
"header": "Header 1",
"line": "line 1 of Header 1"
},
{
"header": "Header 1",
"line": "line 2 of Header 1"
},
{
"header": "Header 2",
"line": "line 1 of Header 2"
},
{
"header": "Header 2",
"line": "line 2 of Header 2"
},
{
"header": "Header 3",
"line": "line 1 of Header 3"
},
{
"header": "Header 3",
"line": "line 2 of Header 3"
}
];
// This will give out our final widget with header and data
Widget get myWidget{
// Consists of all the Widgets
List<Widget> _widget = [];
// our new data to contain {"header": [line]}
Map _newData = {};
// adding the same to it
_data.forEach((element){
if(_newData.containsKey(element["header"])){
_newData[element["header"]].add(element["line"]);
}else{
_newData[element["header"]] = [element["line"]];
}
});
// now finally traversing through our _newData to add the widget accordingly
_newData.forEach((k,v){
// This will add the subitems of the particular Header
List<Widget> _getWidget = [];
// Adding header items to the Container
_widget.add(
Container(
width: double.infinity,
color: Colors.grey,
child: Text(k, textAlign: TextAlign.center)
));
// traversing through line array for a particular array
// And adding to the Widget with the border underline
v.forEach((data){
_getWidget.add(
Text(data)
);
_getWidget.add(
Divider(height: 1.0, color: Colors.grey[300])
);
});
// Finally adding the list of lines under the header
_widget.add(
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _getWidget
)
);
});
// returning the final widget in Column
// Since our _widget is a list of widgets
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _widget
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: double.infinity,
width: double.infinity,
child: this.myWidget
)
);
}
}
FINAL OUTCOME
This is how, your app would look like:
Upvotes: 2