From 1585c69bb4e437b2f1bb241d79a25e5601aec474 Mon Sep 17 00:00:00 2001 From: Atree Date: Sat, 27 Mar 2021 21:02:37 +0000 Subject: [PATCH] Implemented a reusable button, ascending & descending & allows for multiple data types using the comparable interface --- example/lib/main.dart | 73 +++++++++++------------------------ lib/SortableButton.dart | 74 ++++++++++++++++++++++++++++++++++++ lib/multi_sort.dart | 58 ++++++++++++++++++++-------- pubspec.yaml | 2 + test/multisort_test.dart | 82 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 222 insertions(+), 67 deletions(-) create mode 100644 lib/SortableButton.dart create mode 100644 test/multisort_test.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 0d69e42..751c49f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:multi_sort/SortableButton.dart'; import "package:multi_sort/multi_sort.dart"; void main() { @@ -6,14 +7,8 @@ void main() { } class MyApp extends StatelessWidget { - @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), home: ExampleApp(), ); } @@ -22,12 +17,11 @@ class MyApp extends StatelessWidget { class ExampleApp extends StatefulWidget { ExampleApp({Key key}) : super(key: key); - @override _ExampleAppState createState() => _ExampleAppState(); } /// Class of Items -class Items { +class Items implements Sortable { String name; int ram; int price; @@ -35,49 +29,33 @@ class Items { Items(this.name, this.ram, this.price, this.storage); ///Mapping the properties - Map _toMap() { + Map sortableFields() { return {'name': name, 'price': price, 'ram': ram, 'storage': storage}; } - - ///get function to get the properties of Item - dynamic get(String propertyName) { - var _mapRep = _toMap(); - if (_mapRep.containsKey(propertyName)) { - return _mapRep[propertyName]; - } - throw ArgumentError('propery not found'); - } } class _ExampleAppState extends State { //List of Items - List items = [ - Items("real me 6", 6, 18999, 128), + var data = [ + Items("real me 6", 6, 18999, 1), Items("real me 6", 8, 19999, 128), Items("real Note 8", 7, 16999, 128), - Items("oppo a9", 4, 13999, 128), + Items("oppo a9", 4, 13999, 64), Items("real me 6 pro", 6, 17999, 64), Items("Oppo 5as", 2, 8999, 32), Items("Real me 5i", 4, 10999, 64), Items("Poco x2", 6, 18500, 128), ]; - // Temp List for sorting - List sortingList = []; - //Criteria List - List criteria = [false, false]; - //prefrrence List - List preferrence = ['ram', 'storage']; - @override - void initState() { - // TODO: implement initState - super.initState(); + var sortedFields = []; + + void setSortedFields(List sortedFields) { setState(() { - sortingList = items; + this.sortedFields = sortedFields; + this.data = data.multisort(sortedFields); }); } - @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( @@ -85,19 +63,24 @@ class _ExampleAppState extends State { ), body: Column( children: [ - _buttons(), + Row(children: [ + SortableButton(sortedFields, SortField('name'), setSortedFields), + SortableButton(sortedFields, SortField('price'), setSortedFields), + SortableButton(sortedFields, SortField('ram'), setSortedFields), + SortableButton(sortedFields, SortField('storage'), setSortedFields), + ]), ListView.builder( shrinkWrap: true, - itemCount: sortingList.length, + itemCount: data.length, itemBuilder: (BuildContext context, int i) { return Card( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(sortingList[i].name), - Text(sortingList[i].price.toString()), - Text(sortingList[i].ram.toString()), - Text(sortingList[i].storage.toString()), + Text(data[i].name), + Text(data[i].price.toString()), + Text(data[i].ram.toString()), + Text(data[i].storage.toString()), ], ), ); @@ -106,16 +89,4 @@ class _ExampleAppState extends State { ), ); } - - Widget _buttons() { - return RaisedButton( - onPressed: () { - setState(() { - ///Sorting using MultiSort - sortingList.multisort(criteria, preferrence); - }); - }, - child: Text('Sort by preferrence'), - ); - } } diff --git a/lib/SortableButton.dart b/lib/SortableButton.dart new file mode 100644 index 0000000..dbbc7be --- /dev/null +++ b/lib/SortableButton.dart @@ -0,0 +1,74 @@ +import 'package:dartx/dartx.dart'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import 'multi_sort.dart'; + +/// A button to go with multi-sort to toggle sorting on multiple fields +class SortableButton extends StatelessWidget { + /// The current list of sorted fields + final List sortedFields; + + /// This field + final SortField field; + + /// The text for the button label + final String text; + + /// A callback to set the altered sortedFields list + /// + /// '''void setSortedFields(List sortedFields) { + // setState(() { + // this.sortedFields = sortedFields; + // this.data = data.multisort(sortedFields); + // }); + // }''' + final void Function(List) onPressed; + + SortableButton(this.sortedFields, this.field, this.onPressed, {this.text}); + + Widget build(BuildContext context) { + SortField thisSortedField; + int index; + + sortedFields.forEachIndexed((e, i) { + if (e.fieldName == field.fieldName) { + index = i; + thisSortedField = e; + } + }); + + return TextButton( + onPressed: () { + var newSortedFields = []; + + if (thisSortedField == null) // + newSortedFields = [...sortedFields, field]; + else { + if (thisSortedField.isAscending) // + newSortedFields = sortedFields.map((e) => e.fieldName != thisSortedField.fieldName ? e : SortField(e.fieldName, isAscending: false)).toList(); + else + newSortedFields = sortedFields.where((e) => e.fieldName != thisSortedField.fieldName).toList(); + } + + onPressed(newSortedFields); + }, + child: Row( + children: [ + if (thisSortedField != null) // + ...[ + Icon( + thisSortedField.isAscending ? FontAwesomeIcons.angleUp : FontAwesomeIcons.angleDown, + size: 15, + ), + Text( + index.toString(), + textScaleFactor: 0.6, + ), + ], + Text(this.text ?? field.fieldName), + ], + ), + ); + } +} diff --git a/lib/multi_sort.dart b/lib/multi_sort.dart index 0a41f77..0d84abb 100644 --- a/lib/multi_sort.dart +++ b/lib/multi_sort.dart @@ -1,27 +1,49 @@ library multi_sort; -extension MultiSort on List { - multisort(List criteria, dynamic preferrence) { - if (preferrence.length == 0 || criteria.length == 0 || this.length == 0) - return this; - if (preferrence.length != criteria.length) { - print('Criteria length is not equal to preferrence'); +///get function to get the properties of Item +Comparable getComparableField(String propertyName, Sortable sortable) { + var _mapRep = sortable.sortableFields(); + if (_mapRep.containsKey(propertyName)) { + return _mapRep[propertyName]; + } + throw ArgumentError('propery not found'); +} + +abstract class Sortable { + ///A list of strings and their textual ids eg + /// + /// {'name': name, 'price': price, 'ram': ram, 'storage': storage}; + Map sortableFields(); +} + +class SortField { + final String fieldName; + final bool isAscending; + + SortField(this.fieldName, {this.isAscending = true}); +} + +extension MultiSort on List { + List multisort(List sortedFields) { + if (sortedFields.length == 0) // return this; - } - int compare(int i, dynamic a, dynamic b) { - if (a.get(preferrence[i]) == b.get(preferrence[i])) - return 0; - else if (a.get(preferrence[i]) > b.get(preferrence[i])) - return criteria[i] ? 1 : -1; - else - return criteria[i] ? -1 : 1; + int compare(int i, Sortable a, Sortable b) { + var valueA = getComparableField(sortedFields[i].fieldName, a); + var valueB = getComparableField(sortedFields[i].fieldName, b); + + var result = valueA.compareTo(valueB); + + if (!sortedFields[i].isAscending) // + return result * -1; + + return result; } int sortall(a, b) { int i = 0; int result; - while (i < preferrence.length) { + while (i < sortedFields.length) { result = compare(i, a, b); if (result != 0) break; i++; @@ -29,6 +51,10 @@ extension MultiSort on List { return result; } - this.sort((a, b) => sortall(a, b)); + final list = this.toList(); + + list.sort((a, b) => sortall(a, b)); + + return list; } } diff --git a/pubspec.yaml b/pubspec.yaml index 8234515..fe4982a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,8 @@ environment: dependencies: flutter: sdk: flutter + dartx: ^0.6.0 + font_awesome_flutter: ^9.0.0 dev_dependencies: flutter_test: diff --git a/test/multisort_test.dart b/test/multisort_test.dart new file mode 100644 index 0000000..bbac922 --- /dev/null +++ b/test/multisort_test.dart @@ -0,0 +1,82 @@ +//flutter test --plain-name=LessonSplitter + +import 'package:flutter_test/flutter_test.dart'; +import 'package:multi_sort/multi_sort.dart'; + +/// Class of Items +class Items implements Sortable { + String name; + int ram; + Items(this.name, this.ram); + + String toString() => // + "name: ${this.name}, ram: ${this.ram}"; + + Map sortableFields() => // + { + 'name': name, + 'ram': ram, + }; +} + +void main() { + group("multiSort", () { +// List unsorted; + + var unsorted = [ + Items("b", 1), + Items("b", 128), + Items("a", 64), + ]; + +// setUp(() { +// unsorted = [ +// Items("b", 1), +// Items("b", 128), +// Items("a", 64), +// ]; +// }); + + test("two asc", () { + var sortedFields = [ + SortField('ram'), + SortField('name'), + ]; + var sorted = unsorted.multisort(sortedFields); + var expected = [ + Items("b", 1), + Items("a", 64), + Items("b", 128), + ]; + + expect(sorted.toString(), expected.toString()); + }); + + test("two asc & desc", () { + var sortedFields = [ + SortField('name'), + SortField('ram', isAscending: false), + ]; + var sorted = unsorted.multisort(sortedFields); + var expected = [ + Items("a", 64), + Items("b", 128), + Items("b", 1), + ]; + + expect(sorted.toString(), expected.toString()); + }); + + test("sort set to null", () { + var sortedFields = []; + var sorted = unsorted.multisort(sortedFields); + var expected = [ + Items("b", 1), + Items("b", 128), + Items("a", 64), + ]; + + expect(sorted.toString(), expected.toString()); + }); + }); +}