Allow upto 3 decimal points value in flutter InputTextType.number in flutter - user-interface

Below is the code I have tried but it completely fails. If I Remove the WhitelistingTextInputFormatter, I get the Number keyboard and I can insert numbers and periods. but the number of period I can use is more than one, which I need to limit at just one. how to do this?
TextField(
controller: _weightCtr,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(new RegExp('[\\-|\\ ]')),
WhitelistingTextInputFormatter(new RegExp('^\d+[\.\,]\d+\$')),
],
decoration: InputDecoration(
hintText: "Please enter a valid weight for this trip",
),
style: Theme.of(context).textTheme.title.copyWith(
fontWeight: FontWeight.w300,
fontSize: 14,
),
),

You can copy paste run full code below
You can extend TextInputFormatter and remove extra dot
In working demo you can see when extra dot will not show on screen
code snippet
class NumberRemoveExtraDotFormatter extends TextInputFormatter {
NumberRemoveExtraDotFormatter({this.decimalRange = 3})
if (nValue.split('.').length > 2) {
List<String> split = nValue.split('.');
nValue = split[0] + '.' + split[1];
}
...
TextField(
controller: _weightCtr,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
NumberRemoveExtraDotFormatter()
//BlacklistingTextInputFormatter(new RegExp('[\\-|\\ ]')),
//WhitelistingTextInputFormatter(new RegExp('^\d+[\.\,]\d+\$')),
],
decoration: InputDecoration(
hintText: "Please enter a valid weight for this trip",
),
style: Theme.of(context).textTheme.title.copyWith(
fontWeight: FontWeight.w300,
fontSize: 14,
),
),
working demo
full code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math' as math;
class NumberRemoveExtraDotFormatter extends TextInputFormatter {
NumberRemoveExtraDotFormatter({this.decimalRange = 3})
: assert(decimalRange == null || decimalRange > 0);
final int decimalRange;
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
String nValue = newValue.text;
TextSelection nSelection = newValue.selection;
Pattern p = RegExp(r'(\d+\.?)|(\.?\d+)|(\.?)');
nValue = p
.allMatches(nValue)
.map<String>((Match match) => match.group(0))
.join();
if (nValue.startsWith('.')) {
nValue = '0.';
} else if (nValue.contains('.')) {
if (nValue.substring(nValue.indexOf('.') + 1).length > decimalRange) {
nValue = oldValue.text;
} else {
if (nValue.split('.').length > 2) {
List<String> split = nValue.split('.');
nValue = split[0] + '.' + split[1];
}
}
}
nSelection = newValue.selection.copyWith(
baseOffset: math.min(nValue.length, nValue.length + 1),
extentOffset: math.min(nValue.length, nValue.length + 1),
);
return TextEditingValue(
text: nValue, selection: nSelection, composing: TextRange.empty);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
TextEditingController _weightCtr = TextEditingController();
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: _weightCtr,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
NumberRemoveExtraDotFormatter()
//BlacklistingTextInputFormatter(new RegExp('[\\-|\\ ]')),
//WhitelistingTextInputFormatter(new RegExp('^\d+[\.\,]\d+\$')),
],
decoration: InputDecoration(
hintText: "Please enter a valid weight for this trip",
),
style: Theme.of(context).textTheme.title.copyWith(
fontWeight: FontWeight.w300,
fontSize: 14,
),
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Related

Identify search widget using key in widget test?

I try to create a widget test. But I can't understand how to identify widgets can find the key in the main code and do a test?
Main. dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Reversi',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var _controller;
var _reversed;
#override
void initState() {
super.initState();
_controller = TextEditingController();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Reversi'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextField(
controller: _controller,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "Enter string to reverse"),
),
const SizedBox(height: 10.0),
if (_reversed != null) ...[
Text(
_reversed,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
const SizedBox(height: 10.0),
],
RaisedButton(
child: Text("Reverse"),
onPressed: () {
if (_controller.text.isEmpty) return;
setState(() {
_reversed = reverseString(_controller.text);
});
},
),
],
),
),
);
}
}
String reverseString(String initial) {
return initial.split('').reversed.join();
}
This is the widget test code I created. Is this code correct to find the key in the main code and do the test?
main_widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_testing/main.dart';
import 'package:flutter_test/flutter_test.dart';
void main(){
testWidgets('Reverse string widget test', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
var textField = find.byType(TextField);
expect(textField, findsOneWidget);
await tester.enterText(textField, 'Hello');
expect(find.text('Hello'), findsOneWidget);
var button = find.text("Reverse");
expect(button,findsOneWidget);
await tester.tap(button);
await tester.pump();
expect(find.text("olleH"),findsOneWidget);
});
}
How can we identify whether the widget can find the key in the main code and do a test? Can someone explain to me if this code is correct?

Cannot get proper value in validator in flutter

Steps to Reproduce
Here is the dartpad link go to it.
Enter any values to the Fields calculate the answer.
Add 0. to the start any Field
Expected results: validator should return 0.xyz
Actual results: validator returns 0xyz
Work around found: using onSaved solves this
Can anyone tell me why is it happening or is it bug in flutter then I will a new issue in the flutter repo
I'm printing the value coming from validator and in onChanged parsing string value to int.
Edit
when input 23 and then inert 0. , this line amount = int.parse(val); execute and stop and then click calculate button, so amount is 23 and value's runtimeType is String 023
TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
print("amount $amount");
print(" ${value.runtimeType}");
print("validator amount value $value ");
if (value.isEmpty) {
return "Enter some amount";
} else if (double.parse(value).toInt() <= 0) {
return "Amount should be greater than 0";
}
return null;
},
onChanged: (val){
print("val1 $val");
amount = int.parse(val);
print("amount $amount");
}
You can copy paste run full code below
The reason why validator not return 0.xyz
Because onChanged executed before validator
So validator receive truncated value by int.parse(value)
If you remove amount = int.parse(value); validator will get correct value but
this condition if (int.parse(value) <= 0) will get Invalid radix-10 number
You need to change to if (double.parse(value).toInt() <= 0)
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.purple,
buttonTheme: ButtonThemeData(
buttonColor: Colors.purple.shade400,
),
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: BillSpitApp(),
);
}
}
class BillSpitApp extends StatefulWidget {
BillSpitApp({Key key}) : super(key: key);
#override
_BillSpitAppState createState() => _BillSpitAppState();
}
class _BillSpitAppState extends State<BillSpitApp> {
int amount;
int numOfPersons;
double splitAmount;
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Bill split app"),
),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
splitAmount == null
? "Fill the details and click on calculate to get your bill split"
: "Bill split is ${splitAmount.toStringAsFixed(2)}",
style: TextStyle(
fontSize: 25,
),
textAlign: TextAlign.center,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
print("validator amount value $value");
if (value.isEmpty) {
return "Enter some amount";
} else if (double.parse(value).toInt() <= 0) {
return "Amount should be greater than 0";
}
return null;
},
onSaved: (value) {
print("onSaved amount value $value");
amount = int.parse(value);
//int.parse(value);
},
decoration: InputDecoration(
labelText: "Amount",
hintText: "1000",
border: OutlineInputBorder(),
),
),
),
TextFormField(
validator: (value) {
print("person value $value");
if (value.isEmpty) {
return "Enter number of persons";
} else if (double.parse(value).toInt() <= 0) {
return "Number should be greater than 0";
}
return null;
},
onSaved: (value) {
print("onsave numOfPersons $value");
numOfPersons = int.parse(value);
},
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: "Number of persons",
hintText: "5",
border: OutlineInputBorder(),
),
),
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: RaisedButton(
child: Text(
"Calculate",
style: TextStyle(color: Colors.white),
),
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
print("calculate $amount");
print("calculate $numOfPersons");
setState(() {
splitAmount = amount / numOfPersons;
});
}
},
),
),
],
),
),
),
);
}
}
It is not flutter bug however when you take amount as integer any value starts with 0, flutter takes it as 0.
if (value.isEmpty) {
return "Enter some amount";
} else if (double.parse(value) <= 0) {
return "Amount should be greater than 0";
}
return null;
and
onChanged: (value) {
amount = double.parse(value);
},

Changing the AppBar colour when a new tab is selected (Flutter)

I have an app with a tab bar and to make the UI better, I wanted to make it so that the colour changes when you got to another tab. How do I make it so that when I click or swipe to another tab, let's say the yellow tab, the whole appbar changes to that colour?
Code:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ColorChange>(
create: (context) => ColorChange(),
child: MaterialApp(
theme: ThemeData.light(),
home: SimpleTab(),
),
);
}
}
class _TestPageState extends State<TestPage> {
TabController controller;
class ColorChange extends ChangeNotifier {
Color color = colors[0];
Color getColor() {
return color;
}
void changeColor() {
color = colors[controller.index];
print(color);
notifyListeners();
}
}
List<Color> colors = const [
Colors.green,
Colors.yellow,
Colors.red,
Colors.blue,
Colors.deepOrange,
Colors.deepPurple,
];
class SimpleTab extends StatefulWidget {
#override
_SimpleTabState createState() => _SimpleTabState();
}
class _SimpleTabState extends State<SimpleTab>
with SingleTickerProviderStateMixin {
Tester tester = Tester();
#override
void initState() {
super.initState();
controller = TabController(length: colors.length, vsync: this);
controller.addListener(ColorChange().changeColor);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Simple Tab Demo"),
backgroundColor: Provider.of<ColorChange>(context).getColor(),
bottom: TabBar(
controller: controller,
tabs: [
Tab(
text: 'Green',
),
Tab(
text: 'Yellow',
),
Tab(
text: 'Red',
),
Tab(
text: 'Blue',
),
Tab(
text: 'Orange',
),
Tab(
text: 'Purple',
),
],
isScrollable: true,
),
),
body: TabBarView(
controller: controller,
children: <Widget>[
Container(
// child: WidgetThing(tester: tester),
),
Container(
// child: WidgetThing(tester: tester),
),
Container(
// child: WidgetThing(tester: tester),
),
Container(
// child: WidgetThing(tester: tester),
),
Container(
// child: WidgetThing(tester: tester),
),
Container(
// child: WidgetThing(tester: tester),
),
],
),
);
}
}
This is just a very simplified demo of my real app. My real app deals with a lot of data fetched from APIs, hence it is probably better if setstate() was not used, because re-building the whole widget may call http requests unnecessarily.
You can try this
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Demo App',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: SimpleTab(),
);
}
}
class CustomTab {
const CustomTab({this.title, this.color});
final String title;
final Color color;
}
class SimpleTab extends StatefulWidget {
#override
_SimpleTabState createState() => _SimpleTabState();
}
class _SimpleTabState extends State<SimpleTab>
with SingleTickerProviderStateMixin {
TabController controller;
List<CustomTab> tabs = const <CustomTab>[
const CustomTab(title: 'Home', color: Colors.deepOrangeAccent),
const CustomTab(title: 'Setting', color: Colors.blueGrey),
const CustomTab(title: 'Map', color: Colors.teal),
];
CustomTab selectedTab;
#override
void initState() {
super.initState();
controller = new TabController(length: tabs.length, vsync: this);
controller.addListener(_select);
selectedTab = tabs[0];
}
void _select() {
setState(() {
selectedTab = tabs[controller.index];
});
}
#override
Widget build(BuildContext context) {
textStyle() {
return new TextStyle(color: Colors.white, fontSize: 30.0);
}
return new Scaffold(
appBar: new AppBar(
title: new Text("Smiple Tab Demo"),
backgroundColor: selectedTab.color,
bottom: new TabBar(
controller: controller,
tabs: tabs
.map((e) => new Tab(
text: e.title,
))
.toList()),
),
body: new TabBarView(
controller: controller,
children: tabs
.map(
(e) => new Container(
color: e.color,
child: new Center(
child: new Text(
e.title,
style: textStyle(),
),
),
),
)
.toList()),
);
}
}

flutter how to make Hero like animation between widgets on the same page

I have a button with a text, and when I pressed the button, a text widget with the same text is added to the same page.
I'd like to add Hero like animation between them.
I guess what I need is SlideTransition, but I don't know how to slide from one widget position to another widget position.
Is it possible to do? What widget (or class) should I look into?
Here's the code I want to do (but doesn't work since Hero doesn't work on the same page widgets):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> text = [];
String buttonTag = "0";
#override
Widget build(BuildContext context) {
List<Widget> textWidgets = [];
for (int i = 0; i < text.length; ++i) {
textWidgets.add(
Padding(
padding: const EdgeInsets.all(8.0),
child: Hero(tag: "${i}", child: Text(text[i])),
)
);
}
return SafeArea(
child: Scaffold(
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
child: Hero(
tag: buttonTag,
child: Text("abcde${text.length}")),
onPressed: () {
setState(() {
text.add("abcde${text.length}");
buttonTag = "${text.length}";
});
},
)
] + textWidgets,
),
),
),
);
}
}
Not exactly the answer to the question but instead of Hero (if it's possible) you can use AnimatedList to get the same result.
Code snippet
import 'package:flutter/material.dart';
import 'dart:math';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
final List<Text> _textWidgets = [];
var rng = new Random();
_addItem() {
setState(() {
_listKey.currentState.insertItem(_textWidgets.length,
duration: const Duration(milliseconds: 500));
int id = rng.nextInt(100);
_textWidgets.add(Text('item $id'));
});
}
Widget _buildItem(
BuildContext context, Text item, Animation<double> animation) {
final offsetAnimation = Tween<Offset>(
begin: Offset(1.0, 0.0),
end: Offset(0.0, 0.0),
).animate(animation);
return SlideTransition(
position: offsetAnimation,
child: SizedBox(
height: 50.0,
child: Center(
child: item,
),
),
);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: <Widget>[
RaisedButton(
child: Text("Add Item"),
onPressed: () {
setState(() {
_addItem();
});
},
),
Expanded(
child: AnimatedList(
key: _listKey,
initialItemCount: _textWidgets.length,
itemBuilder: (context, index, animation) {
return _buildItem(context, _textWidgets[index], animation);
},
),
),
],
),
),
);
}
}
I think it's possible by this llibrary which named: LocalHero
Implement your self
The AnimationController, Tween, AnimatedBuilder are key components.
This is a sample and code about this.
Don't use AnimatedController.animate in AnimatedBuilder builder.
evaluate is enough. Because the builder function is called every ticker frame.
Use AnimationController.animate as class member field.
class _AuthorizedState extends State<_Authorized> with SingleTickerProviderStateMixin {
late final _menuAC = AnimationController(vsync: this, duration: 200.ms);
late final isFilterOpen = ValueNotifier(false)..addListener(_handleFilterOpenChanged);
late final filterColor =
ColorTween(begin: context.color.primaryContainer, end: context.color.secondaryContainer)
.animate(_menuAC);
late final filterBorderRadius = Tween<double>(begin: 12, end: 0).animate(_menuAC);
void _handleFilterOpenChanged() {
print(isFilterOpen.value);
if (isFilterOpen.value) {
_menuAC.forward();
} else {
_menuAC.reverse();
}
}
#override
void dispose() {
_menuAC.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AppScaffold(
child: LayoutBuilder(
builder: (context, cons) => Stack(
children: [
AnimatedBuilder(
animation: _menuAC,
builder: (context, child) {
return Positioned(
bottom: Tween(begin: 16.0, end: 0.0).evaluate(_menuAC),
right: Tween(begin: 16.0, end: 0.0).evaluate(_menuAC),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(12.0),
topRight: const Radius.circular(12.0),
bottomLeft:
Radius.circular(Tween<double>(begin: 12, end: 0).evaluate(_menuAC)),
bottomRight:
Radius.circular(Tween<double>(begin: 12, end: 0).evaluate(_menuAC)),
),
child: Container(
width: Tween(begin: 56.0, end: cons.maxWidth).evaluate(_menuAC),
height: Tween(begin: 56.0, end: min(cons.maxHeight * 0.7, 500.0))
.evaluate(_menuAC),
decoration: BoxDecoration(
color: filterColor.value,
),
child: child,
),
),
);
},
child: Material(
elevation: 24,
color: Colors.transparent,
child: InkWell(
onTap: () {
isFilterOpen.value = !isFilterOpen.value;
},
child: const Center(child: Icon(MdiIcons.filter)),
),
),
),
],
),
),
);
}
}

validation is becomes true before validation is checked in flutter

I have a question about validation. My widget field validation is become true before checked. when I am just open this page validation is automatically becomes true.
I want that validation after user input. but this validation is becoming true before the user entering something inside from the field. so can anyone help me? your help will be appreciated.
Here is the code I've tried.
class BspSignupPage extends StatefulWidget {
static const String routeName = "/bspSignup";
#override
_BspSignupPageState createState() => _BspSignupPageState();
}
class _BspSignupPageState extends State<BspSignupPage>
with AfterLayoutMixin<BspSignupPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
// final TextEditingController _bspPhone = TextEditingController();
final MaskedTextController _bspPhone =
new MaskedTextController(mask: '(000)-000-0000');
final TextEditingController _bspBusinessName = TextEditingController();
final TextEditingController _bspBusinessLegalAddress =
TextEditingController();
final TextEditingController _bspBusinessLicense = TextEditingController();
final TextEditingController _bspLicenseAuthority = TextEditingController();
final TextEditingController _bspEstYear = TextEditingController();
final TextEditingController _bspNumberOfEmployee = TextEditingController();
final TextEditingController _bspBusinessDetailsComment =
TextEditingController();
final TextEditingController _countryCodeController =
new TextEditingController();
BSPSignupRepository _bspSignupRepository = new BSPSignupRepository();
bool bspcheck = false;
BspSignupCommonModel model = BspSignupCommonModel();
int radioValue = -1;
String _alternatephone;
String _businessname;
bool addressenabled = false;
List<dynamic> _type = <dynamic>[];
Map<String, dynamic> _typeValue;
String _establishyear;
String _numberofemployee;
LocationResult _pickedLocation;
DateTime selectedDate = DateTime.now();
bool flexibletime = false;
DateTime date;
TimeOfDay time;
Map<String, dynamic> bspsignupdata = new Map<String, dynamic>();
#override
void initState() {
super.initState();
print(model);
_bspNumberOfEmployee.text = "1";
_bspSignupRepository.getBSTypes().then((businessTypeResponse) {
print('businessTypeResponse');
print(businessTypeResponse);
if (businessTypeResponse['error'] != null) {
} else {
setState(() {
_type = businessTypeResponse['data']['businessTypes'];
});
}
});
setState(() {
date = new DateTime.now().add(new Duration(hours: 1));
time = new TimeOfDay.fromDateTime(date);
});
}
#override
void afterFirstLayout(BuildContext context) {
model = ModalRoute.of(context).settings.arguments;
if (model == null) {
model = new BspSignupCommonModel();
} else {
print('model for edit');
_setExistingDetails(model);
}
}
void _setExistingDetails(bspModel) {
_bspBusinessName.text = bspModel.businessLegalName;
_bspPhone.text = model.businessPhoneNumber;
_bspEstYear.text = model.businessYear;
_bspNumberOfEmployee.text = model.numberofEmployees;
_bspBusinessLegalAddress.text = model.businessLegalAddress;
_typeValue = model.businessTypes;
}
Widget _buildlegalbusinessname() {
return new TudoTextWidget(
controller: _bspBusinessName,
textCapitalization: TextCapitalization.sentences,
prefixIcon: Icon(Icons.business),
labelText: AppConstantsValue.appConst['bspSignup']['legalbusinessname']
['translation'],
hintText: AppConstantsValue.appConst['bspSignup']['legalbusinessname']
['translation'],
validator: (val) =>
Validators.validateRequired(val, "Business legal name"),
onSaved: (val) {
_businessname = val;
bspsignupdata['businessname'] = _businessname;
},
);
}
Widget _buildalternatephone() {
return Row(
children: <Widget>[
new Expanded(
child: new TudoTextWidget(
controller: _countryCodeController,
enabled: false,
prefixIcon: Icon(FontAwesomeIcons.globe),
labelText: "code",
hintText: "Country Code",
),
flex: 2,
),
new SizedBox(
width: 10.0,
),
new Expanded(
child: new TudoNumberWidget(
controller: _bspPhone,
validator: Validators().validateMobile,
labelText: AppConstantsValue.appConst['bspSignup']['alternatephone']
['translation'],
hintText: AppConstantsValue.appConst['bspSignup']['alternatephone']
['translation'],
prefixIcon: Icon(Icons.phone),
onSaved: (val) {
_alternatephone = val;
bspsignupdata['alternatephone'] = _alternatephone;
},
),
flex: 5,
),
],
);
}
Widget _buildestablishedyear() {
return new TudoNumberWidget(
controller: _bspEstYear,
prefixIcon: Icon(FontAwesomeIcons.calendar),
labelText: AppConstantsValue.appConst['bspSignup']['establishedyear']
['translation'],
hintText: AppConstantsValue.appConst['bspSignup']['establishedyear']
['translation'],
validator: Validators().validateestablishedyear,
maxLength: 4,
onSaved: (val) {
_establishyear = val.trim();
bspsignupdata['establishyear'] = _establishyear;
},
);
}
Widget _buildnumberofemployees() {
return new TudoNumberWidget(
controller: _bspNumberOfEmployee,
prefixIcon: Icon(Icons.control_point_duplicate),
labelText: AppConstantsValue.appConst['bspSignup']['numberofemployees']
['translation'],
hintText: AppConstantsValue.appConst['bspSignup']['numberofemployees']
['translation'],
validator: Validators().validatenumberofemployee,
onSaved: (val) {
_numberofemployee = val.trim();
bspsignupdata['numberofemployes'] = _numberofemployee;
},
);
}
Widget _buildbusinesslegaladdress() {
return Row(
children: <Widget>[
new Expanded(
child: new TudoTextWidget(
prefixIcon: Icon(Icons.business),
labelText: AppConstantsValue.appConst['bspSignup']
['businesslegaladdress']['translation'],
hintText: AppConstantsValue.appConst['bspSignup']
['businesslegaladdress']['translation'],
controller: _bspBusinessLegalAddress,
enabled: addressenabled,
validator: (val) =>
Validators.validateRequired(val, "Business legal name"),
),
flex: 5,
),
new SizedBox(
width: 10.0,
),
new Expanded(
child: new FloatingActionButton(
backgroundColor: colorStyles['primary'],
child: Icon(
FontAwesomeIcons.globe,
color: Colors.white,
),
elevation: 0,
onPressed: () async {
LocationResult result = await LocationPicker.pickLocation(
context,
"AIzaSyDZZeGlIGUIPs4o8ahJE_yq6pJv3GhbKQ8",
);
print("result = $result");
setState(() {
_pickedLocation = result;
addressenabled = !addressenabled;
});
// setState(() => _pickedLocation = result);
_bspBusinessLegalAddress.text = _pickedLocation.address;
model.businessGeoLocation = new BusinessGeoLocation(
lat: _pickedLocation.latLng.latitude.toString(),
lng: _pickedLocation.latLng.longitude.toString(),
);
},
),
flex: 2,
),
],
);
}
Widget _buildbusinesstype() {
return FormBuilder(
autovalidate: true,
child: FormBuilderCustomField(
attribute: "Business type",
validators: [FormBuilderValidators.required()],
formField: FormField(
builder: (FormFieldState<dynamic> field) {
return InputDecorator(
decoration: InputDecoration(
prefixIcon: Icon(Icons.perm_identity),
labelText: _type == []
? 'Select Personal Identification type'
: 'Business type',
hintText: "Select Personal Identification type",
errorText: field.errorText,
),
isEmpty: _typeValue == [],
child: new DropdownButtonHideUnderline(
child: new DropdownButton(
// isExpanded: true,
hint: Text("Select Personal Identification type"),
value: _typeValue,
isDense: true,
onChanged: (dynamic newValue) {
print('newValue');
print(newValue);
setState(() {
_typeValue = newValue;
field.didChange(newValue);
});
},
items: _type.map(
(dynamic value) {
return new DropdownMenuItem(
value: value,
child: new Text(value['name']),
);
},
).toList(),
),
),
);
},
)),
);
}
Widget _buildlegalbusinesscheck() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['bspSignup']['legalbusinesscheck']
['translation'],
errortext: AppConstantsValue.appConst['bspSignup']['errortext']
['translation'],
);
}
Widget content(BuildContext context, BspSignupViewModel bspSignupVm) {
final appBar = AppBar(
title: Text("BSP Signup"),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
NavigationHelper.navigatetoBack(context);
},
),
centerTitle: true,
);
final bottomNavigationBar = Container(
color: Colors.transparent,
height: 56,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
_formKey.currentState.reset();
_bspPhone.clear();
_bspBusinessName.clear();
_bspBusinessLicense.clear();
_bspLicenseAuthority.clear();
_bspEstYear.clear();
_bspNumberOfEmployee.clear();
_bspBusinessDetailsComment.clear();
_bspBusinessLegalAddress.clear();
},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
textColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () async {
if (_formKey.currentState.validate()) {
model.businessLegalName = _bspBusinessName.text;
model.businessPhoneNumber = _bspPhone.text;
model.businessYear = _bspEstYear.text;
model.numberofEmployees = _bspNumberOfEmployee.text;
model.businessType = _typeValue['id'];
model.businessLegalAddress = _bspBusinessLegalAddress.text;
model.businessTypes = _typeValue;
print('model');
print(model.licensed);
if (_typeValue['name'].toLowerCase() ==
"Licensed / Registered".toLowerCase()) {
model.isLicensed = true;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BspLicensedSignupPage(
bspSignupCommonModel: model,
),
),
);
} else {
model.isLicensed = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BspUnlicensedSignupPage(
bspSignupCommonModel: model,
),
),
);
}
}
},
),
],
),
);
return Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: Container(
height: double.infinity,
width: double.infinity,
child: Form(
autovalidate: true,
key: _formKey,
child: Stack(
children: <Widget>[
// Background(),
SingleChildScrollView(
padding: const EdgeInsets.all(30.0),
child: new Container(
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildlegalbusinessname(),
_buildalternatephone(),
_buildestablishedyear(),
_buildnumberofemployees(),
SizedBox(
height: 5,
),
_buildbusinesslegaladdress(),
_buildbusinesstype(),
_buildlegalbusinesscheck(),
],
),
),
),
],
),
),
),
);
}
#override
Widget build(BuildContext context) {
return new StoreConnector<AppState, BspSignupViewModel>(
converter: (Store<AppState> store) => BspSignupViewModel.fromStore(store),
onInit: (Store<AppState> store) {
_countryCodeController.text =
store.state.auth.loginUser.user.country.isdCode;
},
builder: (BuildContext context, BspSignupViewModel bspSignupVm) =>
content(context, bspSignupVm),
);
}
}
your widget field validation is become true before checked becuase you have given static flag true to "autovalidate" to solve this issue you have to manage flag variable for that
Example:-
bool _autoValidate = false;
Form(
key: _formKey,
autovalidate: _autoValidate,
child: Container(child:Text("")));
And change flag value when first time validating form
void _buttonClicked(BuildContext context) {
setState(() {
_autoValidate = true;
});
}
Update:-
autovalidate is deprecated from Flutter v1.19
Replace autovalidate with autovalidateMode.autovalidateMode can have one of the below 3 values:
autovalidateMode: AutovalidateMode.disabled: No auto validation will occur.
autovalidateMode: AutovalidateMode.always: Used to auto-validate FormField even without user interaction.
autovalidateMode: AutovalidateMode.onUserInteraction: Used to auto-validate FormField only after each user interaction.

Resources