Mozart Al Khateeb

Full Stack

Mobile

.Net Developer

ERP Developer

Mozart Al Khateeb

Full Stack

Mobile

.Net Developer

ERP Developer

Blog Post

Flutter Filter WordPress Posts by Category (Part 2)

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

Taggs:
Write a comment