Search code examples
flutterrestdartdata-structuresflutter-layout

how to sort data on base of date and avoid data repetition


here is the list which i get from server `

[
  {
    "Name": "101 Working hours",
    "Balance": "8.00",
    "Date": "2022-10-19",
    "ShiftName": "AU128"
  },
  {
    "Name": "102 Bonus pay",
    "Balance": "3:48",
    "Date": "2022-10-19",
    "ShiftName": ""
  },
  {
    "Name": "110 Split Shift",
    "Balance": "1:00",
    "Date": "2022-10-19",
    "ShiftName": ""
  },
  {
    "Name": "111 Wage reduction",
    "Balance": "1:00",
    "Date": "2022-10-19",
    "ShiftName": ""
  },
  {
    "Name": "111 Wage reduction",
    "Balance": "1:00",
    "Date": "2022-10-20",
    "ShiftName": ""
  },
  {
    "Name": "101 Working hours",
    "Balance": "8.00",
    "Date": "2022-10-21",
    "ShiftName": "AU128"
  },
  {
    "Name": "102 Bonus pay",
    "Balance": "3:48",
    "Date": "2022-10-21",
    "ShiftName": ""
  },
  {
    "Name": "110 Split Shift",
    "Balance": "1:00",
    "Date": "2022-10-21",
    "ShiftName": ""
  },
  {
    "Name": "111 Wage reduction",
    "Balance": "1:00",
    "Date": "2022-10-21",
    "ShiftName": ""
  },
]

`

here you can see date is repeating and all i want to avoid date repetition on FE. you can see app Screenshot to get an idea which i get and which i want to achieve.

the data i get.

enter image description here

the data i want to be achieved

enter image description here

I tried to avoid date repetition and insert data on same list if date is same but all the time i get repetition date and data as you can see in image.

my code:

 Expanded(
          child: Consumer<EmployeeWageAccountsProvider>(
              builder: (context, data, child) {
            if (!data.isLoading) {
              int length = data.getEmployeeAccountsData!.length;
              if (data.getEmployeeAccountsData!.isNotEmpty) {
                wageAccountsData = data.getEmployeeAccountsData!;
                return ListView.builder(
                  itemCount: length,
                  shrinkWrap: true,
                  scrollDirection: Axis.vertical,
                  itemBuilder: (context, i) {
                    return WageAccountsCard(
                      date: Helper.formatStringDate(
                          wageAccountsData[i].date!),
                      balance: wageAccountsData[i].balance,
                      name: wageAccountsData[i].name,
                      shiftName: wageAccountsData[i].shiftName,
                    );
                  },
                );
              }
              return noDataFound(context, 50);
            }
            return const WageAccountsShimmer();
          }),
        )

wageacount card

class WageAccountsCard extends StatelessWidget {
  final String? date;
  final String? balance;
  final String? name;
  final String? shiftName;

const WageAccountsCard(
    {Key? key, this.date, this.name, this.balance, 
     this.shiftName})
    : super(key: key);

@override
  Widget build(BuildContext context) {
    return Container(
     margin: const EdgeInsets.all(10),
     padding: const EdgeInsets.all(8),
     decoration: CustomBoxDecoration.cardDecoration(context, 
  shadow: true),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: [
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(
            shiftName!,
            style: wageTextStyle,
          ),
          Text(
            date.toString(),
            style: wageTextStyle,
          ),
        ],
      ),
      SizedBox(
        height: Styles.height(context) * 0.01,
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(
            S.of(context).wage_type,
            style: cTextStyle,
          ),
          Text(
            S.of(context).balance,
            style: cTextStyle,
          ),
        ],
      ),
      const Divider(
        color: Colors.black,
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(
            name!,
            style: cTextStyle,
          ),
          Text(
            balance.toString(),
            style: cTextStyle,
          ),
        ],
      ),
      SizedBox(
          height: Styles.height(context) * 0.01,
         ),
       ],
     ),
    );
  }
}

model class

class EmployeeWageAccountsModel {
   String? name;
   String? balance;
   String? date;
   String? shiftName;

   EmployeeWageAccountsModel({this.name, this.balance, this.date, 
 this.shiftName});

 EmployeeWageAccountsModel.fromJson(Map<String, dynamic> json) {
   name = json['Name'];
   balance = json['Balance'];
   date = json['Date'];
   shiftName = json['ShiftName'];
 }

Map<String, dynamic> toJson() {
   final Map<String, dynamic> data = <String, dynamic>{};
   data['Name'] = name;
   data['Balance'] = balance;
   data['Date'] = date;
   data['ShiftName'] = shiftName;
   return data;
  }
}

parsing data from API

List<EmployeeWageAccountsModel> employeeWageAccountsList =
  List<EmployeeWageAccountsModel>.from(
      employeeWageAccountsResponse.map((model) =>
          EmployeeWageAccountsModel.fromJson(model)));

Solution

  • You can use collection package and group your data with date :

    var grouped = groupBy(xdata, (Map value) => value['Date']);
    

    then you can make list base on grouped. Note that xdata is your list that you get from server.

    grouped is map and contain this:

    {
     2022-10-19: [
        {Name: 101 Working hours, Balance: 8.00, Date: 2022-10-19, ShiftName: AU128}, 
        {Name: 102 Bonus pay, Balance: 3:48, Date: 2022-10-19, ShiftName: }, 
        {Name: 110 Split Shift, Balance: 1:00, Date: 2022-10-19, ShiftName: }, 
        {Name: 111 Wage reduction, Balance: 1:00, Date: 2022-10-19, ShiftName: }
     ], 
     2022-10-20: [
        {Name: 111 Wage reduction, Balance: 1:00, Date: 2022-10-20, ShiftName: }
     ], 
     2022-10-21: [
        {Name: 101 Working hours, Balance: 8.00, Date: 2022-10-21, ShiftName: AU128}, 
        {Name: 102 Bonus pay, Balance: 3:48, Date: 2022-10-21, ShiftName: }, 
        {Name: 110 Split Shift, Balance: 1:00, Date: 2022-10-21, ShiftName: }, 
        {Name: 111 Wage reduction, Balance: 1:00, Date: 2022-10-21, ShiftName: }
     ]
    }
    

    also change your model class to this:

    class EmployeeWageAccountsModel {
      String? date;
      String? shiftName;
      List<Wage>? wages;
    
      EmployeeWageAccountsModel({this.wages, this.date, this.shiftName});
    
      EmployeeWageAccountsModel.fromJson(Map<String, dynamic> json) {
        wages = (json.values.first as List).map((e) => Wage.fromJson(e)).toList();
    
        date = json.keys.first;
        shiftName = json.values.first[0]['ShiftName'];
      }
    
       ...  
    }
    
    class Wage {
      String? name;
      String? balance;
      Wage({this.name, this.balance});
    
      Wage.fromJson(Map<String, dynamic> json) {
        name = json['Name'];
        balance = json['Balance'];
      }
    }
    

    and parse it like this:

    var grouped =
        groupBy(employeeWageAccountsResponse, (Map value) => value['Date']);
    
    List<EmployeeWageAccountsModel> employeeWageAccountsList =
        List<EmployeeWageAccountsModel>.from(grouped.entries
            .map((e) => EmployeeWageAccountsModel.fromJson({e.key: e.value})));
    

    then change your list item to this:

    class WageAccountsCard extends StatelessWidget {
      final String? date;
      final String? shiftName;
      final List<Wage>? wages;
    
      const WageAccountsCard(
          {Key? key,
          this.date,
          this.shiftName,
          this.wages})
          : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: const EdgeInsets.all(10),
          padding: const EdgeInsets.all(8),
          decoration: CustomBoxDecoration.cardDecoration(context, shadow: true),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    shiftName!,
                    style: wageTextStyle,
                  ),
                  Text(
                    date.toString(),
                    style: wageTextStyle,
                  ),
                ],
              ),
              SizedBox(
                height: Styles.height(context) * 0.01,
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    S.of(context).wage_type,
                    style: cTextStyle,
                  ),
                  Text(
                    S.of(context).balance,
                    style: cTextStyle,
                  ),
                ],
              ),
              const Divider(
                color: Colors.black,
              ),
              ListView.builder(
                itemBuilder: (context, index) {
                  return Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        wages![index].name!,
                        style: cTextStyle,
                      ),
                      Text(
                        wages![index].balance.toString(),
                        style: cTextStyle,
                      ),
                    ],
                  );
                },
                itemCount: wages!.length,
                shrinkWrap: true,
              ),
              SizedBox(
                height: Styles.height(context) * 0.01,
              ),
            ],
          ),
        );
      }
    }