I'm a beginner in coding and I'm trying to create an app to practice and learning dart and flutter.
My app has a page with a long ListView (700 items) and I want to make some sort of "page navigator" to split the ListView in 7 (100 items per page). I know about the pagination ListView but I don't link the result. I'm sorry for my poor vocabulary on explaining what I want to do, here's an example
Ultimately, I want that all my items in this "splitted ListView" could be filterable by a search bar.
Here's my list.dart code so far:
class SongList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: _buildListView(context),
);
}
ListView _buildListView(BuildContext context) {
return ListView.separated(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: 700,
itemBuilder: (_, index) {
final count = index + 1;
return new ListTile(
leading: new CircleAvatar(
child: new Text(
"$count",
style: TextStyle(color: kBackgroundColor),
),
backgroundColor: kPrimaryColor,
),
title: new Text("Song #$count"),
trailing: Icon(Icons.navigate_next),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SongDetail(index),
),
);
},
);
},
separatorBuilder: (context, index) {
return Divider();
},
);
}
}
I discovered this package which is amazing! https://pub.dev/packages/number_paginator
So I updated the code from below and replaced the CupertinoSlidingSegmentedControl with the NumberPaginator widget.
Here the code:
child: NumberPaginator(
numberPages: 7,
onPageChange: (int index) {
setState(() {
currentPage = index;
});
},
),
The packages provide even some properties in order to customize the colors and shape of the page indicators.
I hope this will be helpful for someone. Please note that I'm still a learner and if somethig in this code is off and might be better comment and add your contribution.
I didn't find any native "paging widget" in Flutter so I ended up using CupertinoSlidingSegmentedControl for the "paging widget" and CupertinoUserInterfaceLevel for the "body" of the segments/pages.
My song list is provided by a database helper and some queries, I dediced to split the my 700 items list in 7 queries and call them according to the selected segment/page indicator.
This is my code with some useful comments.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; // Required in order to use Cupertino Widgets
import '/assets/data/queries.dart'; // My queries are in another dart file
import '/assets/data/db_tables.dart';
import 'songs_detail.dart';
class SongsBody extends StatefulWidget {
@override
_SongsBodyState createState() => _SongsBodyState();
}
class _SongsBodyState extends State<SongsBody> {
// Change this in order to edit the segment/paging widget, you can put text and icons
final Map<int, Widget> children = const <int, Widget>{
0: Text('1'),
1: Text('2'),
2: Text('3'),
3: Text('4'),
4: Text('5'),
5: Text('6'),
6: Text('7'),
};
// Change this to set the initial segment/page
int currentSegment = 0;
// This is required to update the state when you tap on another segment/page
void onValueChanged(newValue) {
setState(() {
currentSegment = newValue;
});
}
// My queries from queries.dart are called here and used later in the code
final List<Future<List?>> _query = [
QueryCtr().getSongsFrom1To100(),
QueryCtr().getSongsFrom101To200(),
QueryCtr().getSongsFrom201To300(),
QueryCtr().getSongsFrom301To400(),
QueryCtr().getSongsFrom401To500(),
QueryCtr().getSongsFrom501To600(),
QueryCtr().getSongsFrom601To700(),
];
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
// This is the segment control widget (our "paging" widget)
CupertinoSlidingSegmentedControl<int>(
children: children,
onValueChanged: onValueChanged,
groupValue: currentSegment,
),
const Divider(),
// This is the "body" of our segment/page
CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.base,
// My listview is now called with a future builder because I'm getting the data from my database
child: Builder(
builder: (BuildContext context) {
return FutureBuilder<List?>(
// This is the interesting part: I update only the query according to the selected segment
future: _query[currentSegment],
initialData: const [],
builder: (context, snapshot) {
return snapshot.hasData
// If there is some data show them in a listview wrapped in expanded
? Expanded(
child: ListView.separated(
physics: const ScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, i) {
// Here I decided to code a separate widget to build the rows of my listview
return _buildRow(snapshot.data![i]);
},
separatorBuilder: (context, index) {
return const Divider();
},
),
)
// If there is no data return a progress indicator
: const Center(
child: CircularProgressIndicator(),
);
},
);
},
),
),
],
);
}
Widget _buildRow(Raccolta get) {
return ListTile(
leading: CircleAvatar(
child: Text(
get.songId.toString(),
),
),
title: Text(get.songTitle),
trailing: const Icon(
Icons.navigate_next,
color: somecolor,
),
onTap: () {
FocusScope.of(context).unfocus();
int songId = get.songId;
String songTitle = get.songTitle;
String songText = get.songText;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return SongsDetail(songId, songTitle, songText);
},
),
);
},
);
}
}