Dimibe / grouped_list

A Flutter ListView in which items can be grouped into sections.
https://pub.dev/packages/grouped_list
MIT License
395 stars 106 forks source link

I got unexpected behavior when boolean is used as groupBy value #113

Closed pratamatama closed 3 years ago

pratamatama commented 3 years ago

As the title said, I was expecting that the result should group the entity by the expired value which is a getter that returns a boolean based on the calculation of DateTime of the given model. To be clear and simple, I want something like this:

But I got this instead:

Screenshot_20210808-181859_cuciin

My implementation look like this:

GroupedListView<Promo, bool>(
  elements: controller.promos,
  groupBy: (Promo promo) => promo.expired,
  groupHeaderBuilder: (Promo promo) {
    return Container(
      padding: const EdgeInsets.all(20),
      child: Text(
        promo.expired ? 'Expired' : 'Valid',
        style: TextStyle(color: Color(0xFF658DA4)),
      ),
    );
  },
  itemComparator: (promo1, promo2) => promo1.id!.compareTo(promo2.id!),
  itemBuilder: (BuildContext context, Promo promo) {
    return Column(
      children: [
        ListTile(
          onTap: () {},
          leading: CircleAvatar(
            radius: 25,
            backgroundColor: Color(0xFFEBEBEB),
            child: Icon(
              Icons.confirmation_num_outlined,
              color: Colors.black87,
            ),
          ),
          title: Text(
            promo.title!,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(fontWeight: FontWeight.w500),
          ),
          subtitle: Text(
            'Valid until ${promo.formattedExpiredAt}',
            overflow: TextOverflow.ellipsis,
          ),
        ),
        Divider(),
      ],
    );
  },
)

And here is my Promo model, if needed:

import 'package:intl/intl.dart';

class Promo {
  final int? id;
  final String? title;
  final String? description;
  final String? code;
  final DateTime? expiredAt;
  final DateTime? createdAt;
  final DateTime? updatedAt;

  const Promo({
    this.id,
    this.title,
    this.description,
    this.code,
    this.expiredAt,
    this.createdAt,
    this.updatedAt,
  });

  /// Check if the given promo is expired.
  bool get expired {
    final now = DateTime.now();
    return this.expiredAt!.isBefore(now);
  }

  /// Get the human readable version of [expiredAt].
  String get formattedExpiredAt {
    return DateFormat.yMMMMd().format(this.expiredAt!);
  }

  /// Create an instance of [Promo] from json.
  factory Promo.fromJson(Map<String, dynamic> json) {
    return Promo(
      id: json['id'] as int,
      title: json['title'] as String,
      description: json['description'] as String,
      code: json['code'] as String,
      expiredAt: DateTime.parse(json['expired_at'] as String),
      updatedAt: DateTime.parse(json['updated_at'] as String),
    );
  }

  /// Parse list of [Promo] from a list of json.
  static fromList(List listJson) {
    return listJson.map((json) => Promo.fromJson(json)).toList();
  }
}

Am I missed something?

pratamatama commented 3 years ago

Fixed by converting the boolean as int instead. My fault.

GroupedListView<Promo, int>(
  elements: controller.promos,
  groupBy: (Promo promo) => promo.expired ? 1 : -1,
  ...
  itemComparator: (promo1, promo2) => promo1.expired == promo2.expired ? 1 : -1,
  ...
)