Last active
April 16, 2020 18:28
-
-
Save slightfoot/ca8702030d6cb43ab1f7e63e70549ef5 to your computer and use it in GitHub Desktop.
Playing around with IntrinsicWidth and TextField. ValueListenable and ValueListenableBuilder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
void main() => runApp(ExampleApp()); | |
class ExampleApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
theme: ThemeData( | |
primaryColor: Colors.blue, | |
accentColor: Colors.deepOrange, | |
dividerColor: Colors.grey[400], | |
), | |
home: ExampleScreen(), | |
); | |
} | |
} | |
class ExampleScreen extends StatefulWidget { | |
@override | |
ExampleScreenState createState() => ExampleScreenState(); | |
} | |
class ExampleScreenState extends State<ExampleScreen> { | |
List<BasketItem> _items; | |
@override | |
void initState() { | |
super.initState(); | |
_items = <BasketItem>[ | |
BasketItem('SKU-1', 5.99, 1), | |
BasketItem('SKU-2', 10.99, 0), | |
BasketItem('SKU-3', 2.99, 4), | |
]; | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: ListView.separated( | |
itemCount: _items.length, | |
itemBuilder: (BuildContext context, int index) { | |
final item = _items[index]; | |
return BasketItemWidget( | |
item: item, | |
onAddToOrder: () => print('item: $item'), | |
); | |
}, | |
separatorBuilder: (BuildContext context, int index) { | |
return Divider(height: 2.0); | |
}, | |
), | |
); | |
} | |
} | |
class BasketItemWidget extends StatefulWidget { | |
const BasketItemWidget({ | |
Key key, | |
this.item, | |
this.onAddToOrder, | |
}) : super(key: key); | |
final BasketItem item; | |
final VoidCallback onAddToOrder; | |
@override | |
BasketItemWidgetState createState() => BasketItemWidgetState(); | |
} | |
class BasketItemWidgetState extends State<BasketItemWidget> { | |
FocusNode _quantityFocus; | |
TextEditingController _quantityController; | |
@override | |
void initState() { | |
super.initState(); | |
_quantityFocus = FocusNode(); | |
_quantityFocus.addListener(_onFocusChanged); | |
_quantityController = TextEditingController( | |
text: widget.item.quantity.toString(), | |
); | |
} | |
void _onFocusChanged() { | |
if (_quantityFocus.hasFocus) { | |
// force the TextField cursor to the end of the text | |
_updateCursor(); | |
} | |
} | |
@override | |
void dispose() { | |
_quantityFocus.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
final theme = Theme.of(context); | |
return Material( | |
child: Row( | |
children: <Widget>[ | |
Expanded( | |
child: Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 16.0), | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: <Widget>[ | |
ValueListenableBuilder<double>( | |
valueListenable: widget.item.total, | |
builder: (BuildContext context, double value, Widget child) { | |
return Text( | |
'\$${value.toStringAsFixed(2)}', | |
style: theme.textTheme.display1.copyWith( | |
color: Colors.grey[900], | |
fontWeight: FontWeight.bold, | |
), | |
); | |
}, | |
), | |
Text( | |
'Please select required modifiers', | |
style: theme.textTheme.body1.copyWith( | |
color: theme.accentColor, | |
fontWeight: FontWeight.w500, | |
), | |
), | |
], | |
), | |
), | |
), | |
IntrinsicWidth( | |
child: Padding( | |
padding: const EdgeInsets.symmetric(vertical: 8.0), | |
child: Column( | |
children: <Widget>[ | |
Text( | |
'quantity', | |
style: theme.textTheme.body2.copyWith( | |
fontWeight: FontWeight.w500, | |
), | |
), | |
Row( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
InkResponse( | |
onTap: _decrementQuantity, | |
child: Padding( | |
padding: const EdgeInsets.all(10.0), | |
child: Icon(Icons.remove), | |
), | |
), | |
IntrinsicWidth( | |
child: TextField( | |
focusNode: _quantityFocus, | |
controller: _quantityController, | |
onChanged: _changeQuantity, | |
keyboardType: TextInputType.number, | |
inputFormatters: [ | |
WhitelistingTextInputFormatter.digitsOnly, | |
], | |
decoration: InputDecoration( | |
border: InputBorder.none, | |
contentPadding: EdgeInsets.all(8.0), | |
), | |
maxLines: 1, | |
style: theme.textTheme.title.copyWith( | |
color: Colors.grey[900], | |
fontWeight: FontWeight.bold, | |
), | |
), | |
), | |
InkResponse( | |
onTap: _incrementQuantity, | |
child: Padding( | |
padding: const EdgeInsets.all(10.0), | |
child: Icon(Icons.add), | |
), | |
), | |
], | |
), | |
Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 8.0), | |
child: RaisedButton( | |
onPressed: widget.onAddToOrder, | |
child: Text('Add to order'), | |
padding: EdgeInsets.zero, | |
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, | |
), | |
), | |
], | |
), | |
), | |
), | |
], | |
), | |
); | |
} | |
void _changeQuantity(String value) { | |
widget.item.quantity = int.tryParse(value) ?? 0; | |
_updateCursor(); | |
} | |
void _decrementQuantity() { | |
widget.item.decrementQuantity(); | |
_updateCursor(); | |
} | |
void _incrementQuantity() { | |
widget.item.incrementQuantity(); | |
_updateCursor(); | |
} | |
void _updateCursor() { | |
final text = widget.item.quantity.toString(); | |
_quantityController.value = TextEditingValue( | |
text: text, | |
selection: TextSelection.collapsed(offset: text.length), | |
composing: TextRange.collapsed(text.length), | |
); | |
} | |
} | |
class BasketItem { | |
BasketItem(this.sku, this.price, this._quantity) { | |
_updateTotal(); | |
} | |
final _total = ValueNotifier<double>(0); | |
final String sku; | |
final double price; | |
int _quantity; | |
ValueListenable<double> get total => _total; | |
int get quantity => _quantity; | |
set quantity(int value) { | |
_quantity = value; | |
_updateTotal(); | |
} | |
void decrementQuantity() { | |
if (_quantity > 0) { | |
_quantity--; | |
_updateTotal(); | |
} | |
} | |
void incrementQuantity() { | |
_quantity++; | |
_updateTotal(); | |
} | |
void _updateTotal() { | |
_total.value = quantity * price; | |
} | |
@override | |
String toString() => 'BasketItem(0x${hashCode.toRadixString(16)})' | |
'{ $sku, $price * $quantity = ${_total.value} }'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment