Flutter Filter WordPress Posts by Category (Part 2)

It’s been a while since I’ve posted part one and I hope that I’ll have more time to write more posts making this a fully functional app.
Project upgrade
Although you don’t have to upgrade but flutter have developed over this period of time and I’ve upgraded the project to latest version and here is a link that will help you do the migration.
How I created the migration ?
Since the app is too small I’ve created a new project from scratch, replaced the content of (main.dart) and reinstalled the latest version of flutter_wordpress.
Fetch Categories
First we’ll create a function that fetches categories from the server, please note the hide empty parameter that opts out categories that have no posts.
List<wp.Category> categories; Future<List<wp.Category>> fetchCategories() async { var cats = wordPress.fetchCategories( params: wp.ParamsCategoryList( hideEmpty: true, )); return cats; }
This second function calls the fetchCategories function and sets the state, even though we could have merged both functions into one but I prefer to keep them separated so we can move API functions into their own file easily later on.
Future<String> getCategories() async { var res = await fetchCategories(); setState(() { categories = res; // Just to confirm that we are getting the categories form the server categories.forEach((element) { print(element.toJson()); }); }); return "Success!"; }
Load Categories on the initState method
Modify the initState event adding the getCategories function like below
@override void initState() { super.initState(); this.getPosts(); this.getCategories(); }
If you run the app you should see the categories printed in the console.
Building the UI
Creating a custom Dialog
To keep thing simple I’ll create the custom dialog widget in the same file like below.
First lets define some variables that we will need later
class _MyHomePageState extends State<MyHomePage> { List<int> _selectedCategories = List(); // Preserves a list with selected categoty Id's bool _isLoading = false; // We will use this to show a loading indicator when fetching posts
Modify the app bar adding a filter button like below, and I’ve changed the title display to center, the open filter method is responsible for opening the filter dialog, we’ll be creating it in the next step.
appBar: AppBar( title: Text('Mozartec'), backgroundColor: Colors.blueAccent, centerTitle: true, actions: [ IconButton( icon: Icon(Icons.filter_list_sharp), onPressed: () => openFilterDialog(), ), ], ),
Open dialog
The Code below consists of three functions:
- openFilterDialog to show the dialog when clicking the filter Icon.
- buildCategory defines the shape of the category list item.
- toggleSelection to select/unselect categories.
Please read inline comments to better understand how the code is organized.
openFilterDialog() { showDialog( context: context, builder: ( BuildContext context, ) { return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), elevation: 0.0, backgroundColor: Colors.white, child: StatefulBuilder( // We need the stateful builder since Dialog content are not affected by widget setState builder: (BuildContext context, StateSetter setState) => ListView.builder( itemCount: categories == null ? 0 : categories.length, itemBuilder: (BuildContext context, int index) { return buildCategory( index, setState); //Building the categories list view }, ), ), ); }, ).then((value) => getPosts()); // Refresh posts after category filter has changed } Widget buildCategory(int index, StateSetter setState) { var currentItem = categories[index]; return Container( child: ListTile( title: Padding( padding: EdgeInsets.symmetric( vertical: 5.0, ), child: Text(currentItem.name), ), selected: _selectedCategories.any((element) => element == currentItem.id), isThreeLine: false, onTap: () => setState(() => toggleSelection(currentItem)), key: Key(currentItem.id.toString()), ), ); } void toggleSelection(wp.Category currentItem) { if (_selectedCategories.any((element) => element == currentItem.id)) { _selectedCategories.remove(currentItem.id); } else { _selectedCategories.add(currentItem.id); } }
Now that we have everything in place we still have to apply the categories filter to the get posts request and also make use of the _isLoading variable to control when we show the progress bar.
Filter posts by category
List<wp.Post> posts; Future<List<wp.Post>> fetchPosts() async { var posts = wordPress.fetchPosts( postParams: wp.ParamsPostList( context: wp.WordPressContext.view, postStatus: wp.PostPageStatus.publish, orderBy: wp.PostOrderBy.date, order: wp.Order.desc, includeCategories: _selectedCategories), fetchAuthor: true, fetchFeaturedMedia: true, fetchComments: true, fetchCategories: true, fetchTags: true, ); return posts; }
Show loading indicator
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Mozartec'), backgroundColor: Colors.blueAccent, centerTitle: true, actions: [ IconButton( icon: Icon(Icons.filter_list_sharp), onPressed: () => openFilterDialog(), ), ], ), body: _isLoading ? Center( child: CircularProgressIndicator(), ) : ListView.builder( itemCount: posts == null ? 0 : posts.length, itemBuilder: (BuildContext context, int index) { return buildPost(index); //Building the posts list view }, ), ); }
Future<String> getPosts() async { setState(() { _isLoading = true; }); var res = await fetchPosts(); setState(() { posts = res; _isLoading = false; }); return "Success!"; }
And that’s it for today. You can find the source code in this GitHub branch
exceptional article
best