From eabed12da5da2aecbb11991eb9462e170bd95a5e Mon Sep 17 00:00:00 2001 From: Chema Molins Date: Sat, 20 Oct 2018 15:39:39 +0200 Subject: [PATCH 1/5] Make highlight optional and allow to use a decoration. Fixes issue #4. --- example/lib/example.dart | 17 +++++ lib/numberpicker.dart | 133 ++++++++++++++++++++++++++------------- 2 files changed, 108 insertions(+), 42 deletions(-) diff --git a/example/lib/example.dart b/example/lib/example.dart index 5379956..03f5beb 100644 --- a/example/lib/example.dart +++ b/example/lib/example.dart @@ -35,6 +35,19 @@ class _MyHomePageState extends State { NumberPicker integerNumberPicker; NumberPicker decimalNumberPicker; + Decoration _decoration = new BoxDecoration( + border: new Border( + top: new BorderSide( + style: BorderStyle.solid, + color: Colors.black26, + ), + bottom: new BorderSide( + style: BorderStyle.solid, + color: Colors.black26, + ), + ), + ); + _handleValueChanged(num value) { if (value != null) { if (value is int) { @@ -71,6 +84,8 @@ class _MyHomePageState extends State { minValue: 1, maxValue: 5, decimalPlaces: 2, + highlightSelectedValue: false, + decoration: _decoration, onChanged: _handleValueChanged); return new Scaffold( appBar: new AppBar( @@ -104,6 +119,8 @@ class _MyHomePageState extends State { maxValue: 100, step: 10, initialIntegerValue: _currentIntValue, + highlightSelectedValue: false, + decoration: _decoration, ); }, ).then(_handleValueChangedExternally); diff --git a/lib/numberpicker.dart b/lib/numberpicker.dart index b529258..39c4b91 100644 --- a/lib/numberpicker.dart +++ b/lib/numberpicker.dart @@ -24,6 +24,8 @@ class NumberPicker extends StatelessWidget { this.itemExtent = DEFAULT_ITEM_EXTENT, this.listViewWidth = DEFAULT_LISTVIEW_WIDTH, this.step = 1, + this.highlightSelectedValue = true, + this.decoration, }) : assert(initialValue != null), assert(minValue != null), @@ -51,6 +53,8 @@ class NumberPicker extends StatelessWidget { this.decimalPlaces = 1, this.itemExtent = DEFAULT_ITEM_EXTENT, this.listViewWidth = DEFAULT_LISTVIEW_WIDTH, + this.highlightSelectedValue = true, + this.decoration, }) : assert(initialValue != null), assert(minValue != null), @@ -109,6 +113,12 @@ class NumberPicker extends StatelessWidget { ///Currently selected decimal value final int selectedDecimalValue; + ///If currently selected value should be highlighted + final bool highlightSelectedValue; + + ///Decoration to apply to central box where the selected value is placed + final Decoration decoration; + ///Step between elements. Only for integer datePicker ///Examples: /// if step is 100 the following elements may be 100, 200, 300... @@ -170,26 +180,41 @@ class NumberPicker extends StatelessWidget { child: new Container( height: _listViewHeight, width: listViewWidth, - child: new ListView.builder( - controller: intScrollController, - itemExtent: itemExtent, - itemCount: itemCount, - cacheExtent: _calculateCacheExtent(itemCount), - itemBuilder: (BuildContext context, int index) { - final int value = _intValueFromIndex(index); - - //define special style for selected (middle) element - final TextStyle itemStyle = - value == selectedIntValue ? selectedStyle : defaultStyle; - - bool isExtra = index == 0 || index == itemCount - 1; - - return isExtra - ? new Container() //empty first and last element - : new Center( - child: new Text(value.toString(), style: itemStyle), - ); - }, + child: Stack( + children: [ + new ListView.builder( + controller: intScrollController, + itemExtent: itemExtent, + itemCount: itemCount, + cacheExtent: _calculateCacheExtent(itemCount), + itemBuilder: (BuildContext context, int index) { + final int value = _intValueFromIndex(index); + + //define special style for selected (middle) element + final TextStyle itemStyle = + ((value == selectedIntValue) && highlightSelectedValue) + ? selectedStyle + : defaultStyle; + + bool isExtra = index == 0 || index == itemCount - 1; + + return isExtra + ? new Container() //empty first and last element + : new Center( + child: new Text(value.toString(), style: itemStyle), + ); + }, + ), + new Center( + child: new IgnorePointer( + child: new Container( + width: double.infinity, + height: itemExtent, + decoration: decoration, + ), + ), + ), + ], ), ), onNotification: _onIntegerNotification, @@ -208,28 +233,42 @@ class NumberPicker extends StatelessWidget { child: new Container( height: _listViewHeight, width: listViewWidth, - child: new ListView.builder( - controller: decimalScrollController, - itemExtent: itemExtent, - itemCount: itemCount, - itemBuilder: (BuildContext context, int index) { - final int value = index - 1; - - //define special style for selected (middle) element - final TextStyle itemStyle = - value == selectedDecimalValue ? selectedStyle : defaultStyle; - - bool isExtra = index == 0 || index == itemCount - 1; - - return isExtra - ? new Container() //empty first and last element - : new Center( - child: new Text( - value.toString().padLeft(decimalPlaces, '0'), - style: itemStyle), - ); - }, - ), + child: Stack( + children: [ + new ListView.builder( + controller: decimalScrollController, + itemExtent: itemExtent, + itemCount: itemCount, + itemBuilder: (BuildContext context, int index) { + final int value = index - 1; + + //define special style for selected (middle) element + final TextStyle itemStyle = + (value == selectedDecimalValue && highlightSelectedValue) + ? selectedStyle + : defaultStyle; + + bool isExtra = index == 0 || index == itemCount - 1; + + return isExtra + ? new Container() //empty first and last element + : new Center( + child: new Text( + value.toString().padLeft(decimalPlaces, '0'), + style: itemStyle), + ); + }, + ), + new Center( + child: new IgnorePointer( + child: new Container( + width: double.infinity, + height: itemExtent, + decoration: decoration, + ), + ), + ), + ],), ), onNotification: _onDecimalNotification, ); @@ -366,6 +405,8 @@ class NumberPickerDialog extends StatefulWidget { final Widget confirmWidget; final Widget cancelWidget; final int step; + final bool highlightSelectedValue; + final Decoration decoration; ///constructor for integer values NumberPickerDialog.integer({ @@ -375,6 +416,8 @@ class NumberPickerDialog extends StatefulWidget { this.title, this.titlePadding, this.step = 1, + this.highlightSelectedValue = true, + this.decoration, Widget confirmWidget, Widget cancelWidget, }) @@ -391,6 +434,8 @@ class NumberPickerDialog extends StatefulWidget { this.decimalPlaces = 1, this.title, this.titlePadding, + this.highlightSelectedValue = true, + this.decoration, Widget confirmWidget, Widget cancelWidget, }) @@ -427,6 +472,8 @@ class _NumberPickerDialogControllerState extends State { minValue: widget.minValue, maxValue: widget.maxValue, decimalPlaces: widget.decimalPlaces, + highlightSelectedValue: widget.highlightSelectedValue, + decoration: widget.decoration, onChanged: _handleValueChanged); } else { return new NumberPicker.integer( @@ -434,6 +481,8 @@ class _NumberPickerDialogControllerState extends State { minValue: widget.minValue, maxValue: widget.maxValue, step: widget.step, + highlightSelectedValue: widget.highlightSelectedValue, + decoration: widget.decoration, onChanged: _handleValueChanged, ); } From 3bbb5546d9bb3392615d0b21a33b97ace01e115b Mon Sep 17 00:00:00 2001 From: Monika Stobieniecka Date: Thu, 1 Aug 2019 15:39:49 +0200 Subject: [PATCH 2/5] Fixed conflicts resolving --- example/lib/example.dart | 0 example/lib/main.dart | 17 +++++ lib/numberpicker.dart | 133 ++++++++++++++++++++++++--------------- 3 files changed, 98 insertions(+), 52 deletions(-) delete mode 100644 example/lib/example.dart diff --git a/example/lib/example.dart b/example/lib/example.dart deleted file mode 100644 index e69de29..0000000 diff --git a/example/lib/main.dart b/example/lib/main.dart index ec1a2f3..76a661c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -39,6 +39,19 @@ class _MyHomePageState extends State { NumberPicker integerInfiniteNumberPicker; NumberPicker decimalNumberPicker; + Decoration _decoration = new BoxDecoration( + border: new Border( + top: new BorderSide( + style: BorderStyle.solid, + color: Colors.black26, + ), + bottom: new BorderSide( + style: BorderStyle.solid, + color: Colors.black26, + ), + ), + ); + @override Widget build(BuildContext context) { _initializeNumberPickers(); @@ -130,6 +143,8 @@ class _MyHomePageState extends State { minValue: 1, maxValue: 5, decimalPlaces: 2, + highlightSelectedValue: false, + decoration: _decoration, onChanged: (value) => setState(() => _currentDoubleValue = value), ); } @@ -181,6 +196,8 @@ class _MyHomePageState extends State { minValue: 1, maxValue: 5, decimalPlaces: 2, + highlightSelectedValue: false, + decoration: _decoration, initialDoubleValue: _currentDoubleValue, title: new Text("Pick a decimal number"), ); diff --git a/lib/numberpicker.dart b/lib/numberpicker.dart index 643fb31..d1ac4c6 100644 --- a/lib/numberpicker.dart +++ b/lib/numberpicker.dart @@ -28,6 +28,8 @@ class NumberPicker extends StatelessWidget { this.listViewHeight = kDefaultListViewCrossAxisSize, this.step = 1, this.zeroPad = false, + this.highlightSelectedValue = true, + this.decoration, }) : assert(initialValue != null), assert(minValue != null), assert(maxValue != null), @@ -62,8 +64,7 @@ class NumberPicker extends StatelessWidget { this.zeroPad = false, this.highlightSelectedValue = true, this.decoration, - }) - : assert(initialValue != null), + }) : assert(initialValue != null), assert(minValue != null), assert(maxValue != null), assert(maxValue > minValue), @@ -245,32 +246,45 @@ class NumberPicker extends StatelessWidget { child: new Container( height: listViewHeight, width: listViewWidth, - child: new ListView.builder( - scrollDirection: scrollDirection, - controller: intScrollController, - itemExtent: itemExtent, - itemCount: listItemCount, - cacheExtent: _calculateCacheExtent(listItemCount), - itemBuilder: (BuildContext context, int index) { - final int value = _intValueFromIndex(index); - - //define special style for selected (middle) element - final TextStyle itemStyle = - ((value == selectedIntValue) && highlightSelectedValue) - ? selectedStyle - : defaultStyle; - - bool isExtra = index == 0 || index == listItemCount - 1; - - return isExtra - ? new Container() //empty first and last element - : new Center( - child: new Text( - getDisplayedValue(value), - style: itemStyle, - ), - ); - }, + child: Stack( + children: [ + new ListView.builder( + scrollDirection: scrollDirection, + controller: intScrollController, + itemExtent: itemExtent, + itemCount: listItemCount, + cacheExtent: _calculateCacheExtent(listItemCount), + itemBuilder: (BuildContext context, int index) { + final int value = _intValueFromIndex(index); + + //define special style for selected (middle) element + final TextStyle itemStyle = + value == selectedIntValue && highlightSelectedValue + ? selectedStyle + : defaultStyle; + + bool isExtra = index == 0 || index == listItemCount - 1; + + return isExtra + ? new Container() //empty first and last element + : new Center( + child: new Text( + getDisplayedValue(value), + style: itemStyle, + ), + ); + }, + ), + new Center( + child: new IgnorePointer( + child: new Container( + width: double.infinity, + height: itemExtent, + decoration: decoration, + ), + ), + ), + ], ), ), onNotification: _onIntegerNotification, @@ -289,29 +303,42 @@ class NumberPicker extends StatelessWidget { child: new Container( height: listViewHeight, width: listViewWidth, - child: new ListView.builder( - controller: decimalScrollController, - itemExtent: itemExtent, - itemCount: decimalItemCount, - itemBuilder: (BuildContext context, int index) { - final int value = index - 1; - - //define special style for selected (middle) element - final TextStyle itemStyle = - (value == selectedDecimalValue && highlightSelectedValue) - ? selectedStyle - : defaultStyle; - - bool isExtra = index == 0 || index == decimalItemCount - 1; - - return isExtra - ? new Container() //empty first and last element - : new Center( - child: new Text( - value.toString().padLeft(decimalPlaces, '0'), - style: itemStyle), - ); - }, + child: Stack( + children: [ + new ListView.builder( + controller: decimalScrollController, + itemExtent: itemExtent, + itemCount: decimalItemCount, + itemBuilder: (BuildContext context, int index) { + final int value = index - 1; + + //define special style for selected (middle) element + final TextStyle itemStyle = + value == selectedDecimalValue && highlightSelectedValue + ? selectedStyle + : defaultStyle; + + bool isExtra = index == 0 || index == decimalItemCount - 1; + + return isExtra + ? new Container() //empty first and last element + : new Center( + child: new Text( + value.toString().padLeft(decimalPlaces, '0'), + style: itemStyle), + ); + }, + ), + new Center( + child: new IgnorePointer( + child: new Container( + width: double.infinity, + height: itemExtent, + decoration: decoration, + ), + ), + ), + ], ), ), onNotification: _onDecimalNotification, @@ -335,7 +362,9 @@ class NumberPicker extends StatelessWidget { //define special style for selected (middle) element final TextStyle itemStyle = - value == selectedIntValue ? selectedStyle : defaultStyle; + value == selectedIntValue && highlightSelectedValue + ? selectedStyle + : defaultStyle; return new Center( child: new Text( From 63ea54906487c5895dc2eeb9cbd46c80f9b52dbc Mon Sep 17 00:00:00 2001 From: Monika Stobieniecka Date: Thu, 1 Aug 2019 15:57:33 +0200 Subject: [PATCH 3/5] Extracted decoration to widget --- example/lib/main.dart | 2 +- lib/numberpicker.dart | 52 ++++++++++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 76a661c..051239a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -205,7 +205,7 @@ class _MyHomePageState extends State { ).then((num value) { if (value != null) { setState(() => _currentDoubleValue = value); - decimalNumberPicker.animateInt(value); + decimalNumberPicker.animateDecimalAndInteger(value); } }); } diff --git a/lib/numberpicker.dart b/lib/numberpicker.dart index d1ac4c6..d9f1a60 100644 --- a/lib/numberpicker.dart +++ b/lib/numberpicker.dart @@ -275,14 +275,10 @@ class NumberPicker extends StatelessWidget { ); }, ), - new Center( - child: new IgnorePointer( - child: new Container( - width: double.infinity, - height: itemExtent, - decoration: decoration, - ), - ), + _NumberPickerSelectedItemDecoration( + axis: scrollDirection, + itemExtent: itemExtent, + decoration: decoration, ), ], ), @@ -329,14 +325,10 @@ class NumberPicker extends StatelessWidget { ); }, ), - new Center( - child: new IgnorePointer( - child: new Container( - width: double.infinity, - height: itemExtent, - decoration: decoration, - ), - ), + _NumberPickerSelectedItemDecoration( + axis: scrollDirection, + itemExtent: itemExtent, + decoration: decoration, ), ], ), @@ -514,6 +506,34 @@ class NumberPicker extends StatelessWidget { } } +class _NumberPickerSelectedItemDecoration extends StatelessWidget { + final Axis axis; + final double itemExtent; + final Decoration decoration; + + const _NumberPickerSelectedItemDecoration( + {Key key, + @required this.axis, + @required this.itemExtent, + @required this.decoration}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return new Center( + child: new IgnorePointer( + child: new Container( + width: isVertical ? double.infinity : itemExtent, + height: isVertical ? itemExtent : double.infinity, + decoration: decoration, + ), + ), + ); + } + + bool get isVertical => axis == Axis.vertical; +} + ///Returns AlertDialog as a Widget so it is designed to be used in showDialog method class NumberPickerDialog extends StatefulWidget { final int minValue; From 7001b1ca10c14fedb158632efa151b5c0feff4fa Mon Sep 17 00:00:00 2001 From: Monika Stobieniecka Date: Fri, 2 Aug 2019 11:07:51 +0200 Subject: [PATCH 4/5] Added decoration for infinite number picker, added example of decorated number picker --- example/lib/main.dart | 28 ++++++++++++++++++++++---- lib/numberpicker.dart | 47 ++++++++++++++++++++++++++----------------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 051239a..442205d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -33,10 +33,12 @@ class _MyHomePageState extends State { int _currentIntValue = 10; int _currentHorizontalIntValue = 10; int _currentInfIntValue = 10; + int _currentInfIntValueDecorated = 10; double _currentDoubleValue = 3.0; NumberPicker integerNumberPicker; NumberPicker horizontalNumberPicker; NumberPicker integerInfiniteNumberPicker; + NumberPicker integerInfiniteDecoratedNumberPicker; NumberPicker decimalNumberPicker; Decoration _decoration = new BoxDecoration( @@ -101,11 +103,22 @@ class _MyHomePageState extends State { ), Column( children: [ + SizedBox(height: 16), + Text('Default', style: Theme.of(context).textTheme.title), integerInfiniteNumberPicker, new RaisedButton( onPressed: () => _showInfIntDialog(), child: new Text("Current int value: $_currentInfIntValue"), ), + Divider(color: Colors.grey, height: 32), + Text('Decorated', style: Theme.of(context).textTheme.title), + integerInfiniteDecoratedNumberPicker, + Text( + "Current int value: $_currentInfIntValueDecorated", + style: TextStyle( + fontWeight: FontWeight.w600, + ), + ), ], ) ], @@ -138,13 +151,22 @@ class _MyHomePageState extends State { infiniteLoop: true, onChanged: (value) => setState(() => _currentInfIntValue = value), ); + integerInfiniteDecoratedNumberPicker = new NumberPicker.integer( + initialValue: _currentInfIntValueDecorated, + minValue: 0, + maxValue: 99, + step: 10, + infiniteLoop: true, + highlightSelectedValue: false, + decoration: _decoration, + onChanged: (value) => + setState(() => _currentInfIntValueDecorated = value), + ); decimalNumberPicker = new NumberPicker.decimal( initialValue: _currentDoubleValue, minValue: 1, maxValue: 5, decimalPlaces: 2, - highlightSelectedValue: false, - decoration: _decoration, onChanged: (value) => setState(() => _currentDoubleValue = value), ); } @@ -196,8 +218,6 @@ class _MyHomePageState extends State { minValue: 1, maxValue: 5, decimalPlaces: 2, - highlightSelectedValue: false, - decoration: _decoration, initialDoubleValue: _currentDoubleValue, title: new Text("Pick a decimal number"), ); diff --git a/lib/numberpicker.dart b/lib/numberpicker.dart index d9f1a60..62649d3 100644 --- a/lib/numberpicker.dart +++ b/lib/numberpicker.dart @@ -346,25 +346,34 @@ class NumberPicker extends StatelessWidget { child: new Container( height: listViewHeight, width: listViewWidth, - child: new InfiniteListView.builder( - controller: intScrollController, - itemExtent: itemExtent, - itemBuilder: (BuildContext context, int index) { - final int value = _intValueFromIndex(index); - - //define special style for selected (middle) element - final TextStyle itemStyle = - value == selectedIntValue && highlightSelectedValue - ? selectedStyle - : defaultStyle; - - return new Center( - child: new Text( - getDisplayedValue(value), - style: itemStyle, - ), - ); - }, + child: Stack( + children: [ + InfiniteListView.builder( + controller: intScrollController, + itemExtent: itemExtent, + itemBuilder: (BuildContext context, int index) { + final int value = _intValueFromIndex(index); + + //define special style for selected (middle) element + final TextStyle itemStyle = + value == selectedIntValue && highlightSelectedValue + ? selectedStyle + : defaultStyle; + + return new Center( + child: new Text( + getDisplayedValue(value), + style: itemStyle, + ), + ); + }, + ), + _NumberPickerSelectedItemDecoration( + axis: scrollDirection, + itemExtent: itemExtent, + decoration: decoration, + ), + ], ), ), onNotification: _onIntegerNotification, From ea510b074b194ac89dbe4acbae50715fb9763b46 Mon Sep 17 00:00:00 2001 From: Monika Stobieniecka Date: Fri, 2 Aug 2019 11:41:11 +0200 Subject: [PATCH 5/5] Added tests for decorated number picker --- test/integer_infinite_numberpicker_test.dart | 16 +++++++++++++++- test/integer_numberpicker_test.dart | 13 +++++++++++++ test/test_utils.dart | 19 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/test/integer_infinite_numberpicker_test.dart b/test/integer_infinite_numberpicker_test.dart index 9fda01a..1d0fb89 100644 --- a/test/integer_infinite_numberpicker_test.dart +++ b/test/integer_infinite_numberpicker_test.dart @@ -115,4 +115,18 @@ void main() { infiniteLoop: true, expectedDisplayValues: ['09', '10', '00']); }); -} \ No newline at end of file + + testWidgets('Decorated number picker works', (WidgetTester tester) async { + await testNumberPicker( + tester: tester, + minValue: 0, + maxValue: 10, + initialValue: 2, + scrollBy: 2, + expectedValue: 4, + infiniteLoop: true, + highlightSelectedValue: false, + decoration: decoration, + ); + }); +} diff --git a/test/integer_numberpicker_test.dart b/test/integer_numberpicker_test.dart index aca16b2..1e461f8 100644 --- a/test/integer_numberpicker_test.dart +++ b/test/integer_numberpicker_test.dart @@ -100,4 +100,17 @@ void main() { scrollBy: 1, expectedDisplayValues: ['02', '03', '04']); }); + + testWidgets('Decorated number picker works', (WidgetTester tester) async { + await testNumberPicker( + tester: tester, + minValue: 0, + maxValue: 10, + initialValue: 2, + scrollBy: 2, + expectedValue: 4, + highlightSelectedValue: false, + decoration: decoration, + ); + }); } diff --git a/test/test_utils.dart b/test/test_utils.dart index c8f54f0..eaddf70 100644 --- a/test/test_utils.dart +++ b/test/test_utils.dart @@ -2,6 +2,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:numberpicker/numberpicker.dart'; +Decoration decoration = new BoxDecoration( + border: new Border( + top: new BorderSide( + style: BorderStyle.solid, + color: Colors.black26, + ), + bottom: new BorderSide( + style: BorderStyle.solid, + color: Colors.black26, + ), + ), +); + Future testNumberPicker({ WidgetTester tester, int minValue, @@ -13,6 +26,8 @@ Future testNumberPicker({ bool animateToItself = false, Axis axis = Axis.vertical, bool infiniteLoop = false, + Decoration decoration, + bool highlightSelectedValue = true, }) async { int value = initialValue; NumberPicker picker; @@ -26,6 +41,8 @@ Future testNumberPicker({ maxValue: maxValue, step: step, infiniteLoop: infiniteLoop, + decoration: decoration, + highlightSelectedValue: highlightSelectedValue, onChanged: (newValue) => setState(() => value = newValue), ) : NumberPicker.horizontal( @@ -33,6 +50,8 @@ Future testNumberPicker({ minValue: minValue, maxValue: maxValue, step: step, + decoration: decoration, + highlightSelectedValue: highlightSelectedValue, onChanged: (newValue) => setState(() => value = newValue), ); return MaterialApp(