How to unit Test a Flutter TextFormField maxlines - unit-testing

Is it possible to write a unit test that verifies that the maxLines property of a TextFormField is set correctly. I can not find a way to access the property:
i create a TextFormField
final field = TextFormField(
initialValue: "hello",
key: Key('textformfield'),
maxLines: 2,
);
then in the test i get access to the form field with tester.widget
final formfield =
await tester.widget<TextFormField>(find.byKey(Key('textformfield')));
but since the maxLines property is passed to the Builder which returns a Textfield, how can i get access to the textfield.
Or is there an completely other ways to verify this?

I don't know if this is a good solution but as i set the value of my TextFormField i can find the EditableText widget directly.
of this widget i can find test the property maxLines.
final EditableText formfield =
tester.widget<EditableText>(find.text('testvalue'));
expect(formfield.maxLines, 2);

The reason you can't see properties such as maxLines or maxLength is because they belong to the TextField class.
Take a look at the documentation of the TextFormField constructor in the source file:
/// Creates a [FormField] that contains a [TextField].
///
/// When a [controller] is specified, [initialValue] must be null (the
/// default). If [controller] is null, then a [TextEditingController]
/// will be constructed automatically and its `text` will be initialized
/// to [initialValue] or the empty string.
///
/// For documentation about the various parameters, see the [TextField] class
/// and [new TextField], the constructor.
Unfortunately you can't retrieve the TextField object from a TextFormField, you'll have to find the TextField object through a finder instead.
Let's assume you have a form with 2 fields - first name and last name. What you need to do is find all widgets of type TextField, add them to a list and then you can loop through each element of the list and run your test. Here's an example:
testWidgets('Form fields have the correct maximum length and number of lines',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Form(
child: Column(
children: <Widget>[
TextFormField(
key: Key('first_name'),
decoration: InputDecoration(hintText: 'First name'),
maxLines: 1,
maxLength: 50,
obscureText: true,
),
TextFormField(
key: Key('last_name'),
decoration: InputDecoration(hintText: 'Last name'),
maxLines: 1,
maxLength: 25,
),
],
),
),
),
));
List<TextField> formFields = List<TextField>();
find.byType(TextField).evaluate().toList().forEach((element) {
formFields.add(element.widget);
});
formFields.forEach((element) {
expect(element.maxLines, 1);
switch (element.decoration.hintText) {
case 'First name':
expect(element.maxLength, 50);
break;
case 'Last name':
expect(element.maxLength, 25);
break;
}
});
});
If you only one field, you could do this instead:
TextField textField = find.byType(TextField).evaluate().first.widget as TextField;
expect(textField.maxLines, 1);
expect(textField.maxLength, 50);

You can test it with an Integration test. The logic would be to type more text into the TextFormField that the one it's expected.
So we can verify the TextFormField is only allowing 2 character, as follow.
e.g. component:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SingleChildScrollView(
child: MyLoginPage(title: 'Flutter Demo Home Page'),
),
),
);
}
}
class MyLoginPage extends StatefulWidget {
MyLoginPage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyLoginPageState createState() => _MyLoginPageState();
}
class _MyLoginPageState extends State<MyLoginPage> {
String _email;
String _password;
TextStyle style = TextStyle(fontSize: 25.0);
#override
Widget build(BuildContext context) {
final emailField = TextField(
key: Key('textformfield'),
obscureText: false,
maxLength: 2,
style: style,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
prefixIcon: Icon(FontAwesomeIcons.solidEnvelope),
hintText: "Email",
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red[300], width: 32.0),
borderRadius: BorderRadius.circular(97.0))),
onChanged: (value) {
setState(() {
_email = value;
});
},
);
final passwordField = TextField(
obscureText: true,
style: style,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
prefixIcon: Icon(FontAwesomeIcons.key),
hintText: "Password",
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red[300], width: 32.0),
borderRadius: BorderRadius.circular(25.0))),
onChanged: (value) {
setState(() {
_password = value;
});
},
);
return Center(
child: Column(
children: <Widget>[
Container(
color: Colors.yellow[300],
height: 300.0,
),
emailField,
passwordField,
],
),
);
}
}
The test:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_textfields_up/main.dart';
void main() {
testWidgets('Email should be only 2 characters', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
var txtForm = find.byKey(Key('textformfield'));
await tester.enterText(txtForm, '123');
expect(find.text('123'), findsNothing); // 3 characters shouldn't be allowed
expect(find.text('12'), findsOneWidget); // 2 character are valid.
});
}
Please observe I'm sending 3 character, and the TextFormField should only allow 2.
Hope this help.

Related

Flutter property values of a Map all treated as strings

I have a list like this a = [{'one': 'one', 'two': null, 'three': [{'four': 'four'}]}]
I send it to a function to use it in a post request which in the body should receive a Map, so what I did was this to a[0], the problem is that I get this error The getter 'length' was called on null
I start to review and it treats all the property values as if they were Strings, even the nested list 'three': [{'four': 'four'}], I have tried to send the post in this way http.post (url, body: (recurrence [0] as Map)) but it has not worked, it always gives me the same error, even if in the body I put the properties by hand in the body: {'new property': a [0] [' tres']}, how should one act to solve this problem? Thank you very much for your help
Code:
void _ordersGet() async {
await http.get(url).then((value) {
setState(() {
orders = jsonDecode(value.body);
}
}
orders is sent to a new widget: orderList(orders)
orderList is a listView
ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: orders.length,
itemBuilder: (orders, index) {
return return Card(
elevation: 5,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(orders[index]['facts']),
SizedBox(
width: 4,
),
Text('Cantidad : '),
Text(orders[index]['ITEMS'][0]['jeans']),
SizedBox(
width: 4,
),
IconButton(
onPressed: () => _reorderData(context, orders[index]),
icon: Icon(
Icons.replay_outlined,
color: Theme.of(context).accentColor,
)),
],
),
);
},
);
_reorderData is a function that make a get request, the info in shipped to ReorderModal
ReorderModal it only shows the information and has a button
void _reorderData(BuildContext ctx, order) async {
var data;
var url = 'serverAddress/${order['facts']}';
await http.get(url).then((value) {
data = jsonDecode(value.body);
data[0]['CORPORATION'] = order['corporation'];
showModalBottomSheet(
context: ctx,
builder: (_) {
return ReorderModal(data);
});
}).catchError((onError) {});
}
class ReorderModal extends StatelessWidget {
final List data;
ReorderModal(this.data);
void orderSend(orderInfo) async {
var url = 'serverAddress';
await http.post(url, body: orderInfo[0]).then((value) {
print(jsonDecode(value.body));
}).catchError((onError) {
print(onError);
});
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: Column(children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Column(
children: [
ElevatedButton(
onPressed: () {
orderSend(data);
//print(data);
},
child: Text('ONE Click'))
]),
);
}
}
when i press the ONE Click button execute the function orderSend, orderSend make a post request and the problem described above
This is the simplified code, I know it must be something very simple, but it is giving me a lot of work to solve

How to filter a List of Map for creating another List of Map as the result of a search functionality

I am trying to implement a search function in my app for filtering between many entries in a list of map.
The data structure that I have is:
[{Entry: Accident , Definition: An unexpected event or circumstance without deliberate intent.}, {Entry: Accident Insurance , Definition: Insurance for unforeseen bodily injury.}, {Entry: Accident Only , Definition: An insurance contract that provides coverage, singly or in combination, for death, dismemberment, disability, or hospital and medical care caused by or necessitated as a result of accident or specified kinds of accident.}, {Entry: Accident Only or AD&D , Definition: Policies providing coverage, singly or in combination, for death, dismemberment, disability, or hospital and medical care caused by or necessitated as a result of accident or specified kinds of accidents. Types of coverage include student accident, sports accident, travel accident, blanket accident, specific accident or accidental death and dismemberment (ad&d).} ... etc, etc. ]
These are the contents of the .json file:
[
{
"Entry": "Accident ",
"Definition": "An unexpected event or circumstance without deliberate intent."
},
{
"Entry": "Accident Insurance ",
"Definition": "Insurance for unforeseen bodily injury."
},
[... and looooots of many other "Entry", "Definition" pairs like these]
{
"Entry": "Written Premium ",
"Definition": "The contractually determined amount charged by the reporting entity to the policyholder for the effective period of the contract based on the expectation of risk, policy benefits, and expenses associated with the coverage provided by the terms of the insurance contract."
}
]
Each map entry creates one button with an associated definition.
The user is queried for a search query to get only the button(s) that satisfy the query result.
I include the .dart file that I am trying to implement:
import 'package:flutter/material.dart';
import 'listentries.dart';
import 'destination.dart';
import 'dart:convert';
// ignore: must_be_immutable
class searchScreen extends StatefulWidget {
final String searchTerm;
searchScreen({this.searchTerm});
#override
_SearchScreenState createState() => new _SearchScreenState();
}
class _SearchScreenState extends State<searchScreen> {
#override
Widget build(BuildContext context) {
final widgetElements = new ListEntries(); // From listentries.dart
var searchedItems =
widgetElements; // Copy from widgetElements filter out from here
var query;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
title: Text(
"Search your term",
style: TextStyle(fontSize: 20),
),
),
body: Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (query) {
//search is done here
// filterSearchResults(query);
},
decoration: InputDecoration(
labelText: 'Search',
hintText: 'Search your term',
suffixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(25.0),
),
),
),
),
),
Expanded(
child: FutureBuilder(
future: DefaultAssetBundle.of(context)
.loadString('assets/data.json'),
builder: (context, snapshot) {
var entries = json.decode(snapshot.data.toString());
final item = entries.where((e) => e['Entry'] == 'Accident'); //Accident will be changed with query
print(item);
print(entries);
return ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
var entrada = entries[index];
//print(entrada);
return Container(
margin: EdgeInsets.symmetric(vertical: 2.0),
color: Colors.transparent,
width: MediaQuery.of(context).size.width,
height: 60,
child: RaisedButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Destination(
entry: entrada['Entry'],
definition: entrada['Definition'],
),
),
);
},
color: Colors.blue[900],
child: Text(
entrada['Entry'],
style: TextStyle(
color: Colors.white,
fontFamily: 'Raleway',
fontSize: 18.0,
),
),
),
);
},
itemCount: entries == null ? 0 : entries.length,
);
},
),
//child: searchedItems,
),
],
),
),
);
}
}
The issue that I am seeing is that the filtered result (item) is empty and it should contain the entry related to "Accident".
Could you give a hand for the implementation of this search functionality?
Thanks in advance
add .toList() to create new list
final item = entries.where((e) => e['Entry'] == 'Accident').toList();
Here is a stripped down code sample describing how to filter the items:
final List<Map<String, String>> items = [
{'Entry': 'Accident', 'Definition': 'Accident description.'},
{'Entry': 'Accident Insurance', 'Definition': 'Insurance description.'},
];
void main() {
final results = items.where((item) => item['Entry'] == 'Accident');
print(results);
// Iterable<Map<String, String>> ({Entry: Accident, Definition: An unexpected event or circumstance without deliberate intent.})
final result = results.first;
print(result);
// Map<String, String> {Entry: Accident, Definition: An unexpected event or circumstance without deliberate intent.}
}
Note that where returns an Iterable. You can use toList() to get a List of Maps.
Here is a Flutter sample application closer to what you did before:
import 'dart:convert';
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyWidget()));
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future:
DefaultAssetBundle.of(context).loadString('assets/entries.json'),
builder: (context, snapshot) {
final items = json.decode(snapshot.data.toString());
final result =
items.where((item) => item['Entry'] == 'Accident').first;
return Column(
children: [
Text('Accident Definition:'),
Text(result['Definition']),
],
);
},
),
);
}
}
The JSON File I used is here:
[
{
"Entry": "Accident",
"Definition": "Accident description."
},
{
"Entry": "Accident Insurance",
"Definition": "Insurance description."
}
]

Flutter list of Text: How to fix the style of all list items

I am new to flutter and to the concept of Object orientation in general. I am building a list of Text to be used with a CupertinoPicker in flutter, I want to use the same style for all the list items but I don't want to keep repeating the lines and each time specifying the text style.
For example, see the list of car manufacturers below:
import 'package:flutter/material.dart';
TextStyle kStyle = TextStyle(color: Colors.white, fontWeight: FontWeight.w900);
List<Text> manufacturers = [
Text('Toyota', style: kStyle,),
Text('VolksWagen', style: kStyle,),
Text('Nissan', style: kStyle,),
Text('Renault', style: kStyle,),
Text('Mercedes', style: kStyle,),
Text('BMW', style: kStyle,)
];
You see the list items in manufacturers list can get so long with more cars, can I use a class to tell flutter that my style is fixed to kstyle for all the items without explicitly writing style: kstyle for every single line?
Basically We can use DefaultTextStyle widget
Final Result
CupertinoPicker Widget
Common Column Widget
1. The Problem is we need to use CupertinoPicker
which in the library, it is defined as
final Widget result = DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.pickerTextStyle,
child: Stack(
2. Solution : Override Theme
Therefore We are required to defined its style at the very beginning definition of our app
const TextStyle kStyle = TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w900,
);
class FlutterApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Cupertino Picker',
home: ListViewScreen(),
theme: ThemeData(
cupertinoOverrideTheme: CupertinoThemeData( // <---------- this
textTheme: CupertinoTextThemeData(
pickerTextStyle: kStyle,
),
),
),
);
}
}
A. Full Working Code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(FlutterApp());
}
const TextStyle kStyle = TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w900,
);
class FlutterApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Cupertino Picker',
home: ListViewScreen(),
theme: ThemeData(
cupertinoOverrideTheme: CupertinoThemeData( // <---------- this
textTheme: CupertinoTextThemeData(
pickerTextStyle: kStyle,
),
),
),
);
}
}
class ListViewScreen extends StatelessWidget {
final List<Text> manufacturers = [
Text('Toyota'),
Text('VolksWagen'),
Text('Nissan'),
Text('Renault'),
Text('Mercedes'),
Text('BMW')
];
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("Select Car"),
),
child: Container(
height: 200,
child: CupertinoPicker(
itemExtent: 50,
onSelectedItemChanged: (int index) {
print(index);
},
children: manufacturers,
),
),
);
}
}
B. [Optional] Simple use of Default Text Style
List<Text> manufacturers = [
Text('Toyota'),
Text('VolksWagen'),
Text('Nissan'),
Text('Renault'),
Text('Mercedes'),
Text('BMW')
];
const TextStyle kStyle = TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
);
class CarList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: kStyle,
child: Column(
children: manufacturers,
),
);
}
}
You can create a new widget:
class MyTextWidget extends StatelessWidget {
final String text;
const MyTextWidget({Key key, this.text}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(text,style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900),);
}
}
and use it in your list
List<Text> manufacturers = [
MyTextWidget('Toyota'),
MyTextWidget('VolksWagen'),
MyTextWidget('Nissan'),
MyTextWidget('Renault'),
MyTextWidget('Mercedes'),
MyTextWidget('BMW')
];
If you want to change the font in all the application, you must change it from MaterialApp like this:
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
textTheme: TextTheme(
//Use the appropriate TextStyle
),
),);
}
You can create one method for all Text widgets:
Text styledText(String text) => Text(text, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900));
and use it in your list:
List<Text> manufacturers = [
styledText('Toyota'),
styledText('VolksWagen'),
styledText('Nissan'),
styledText('Renault'),
styledText('Mercedes'),
styledText('BMW')
];
You can also create a list of strings
List<String> manufacturers = [
'Toyota',
'VolksWagen',
'Nissan',
'Renault',
'Mercedes',
'BMW',
];
And use tis method when you iterate through your list.
Or you can create a class instead of the method:
class CustomStyledText extends StatelessWidget {
final String text;
const CustomStyledText(this.text, {Key key}) : super(key: key);
TextStyle get _style => TextStyle(color: Colors.white, fontWeight: FontWeight.w900);
#override
Widget build(BuildContext context) => Text(text, style: _style);
}
You can create an extension on the Text widget and use that:
Create an extension:
// extension
extension on Text {
// method to apply style
applyStyle(TextStyle textStyle) {
return Text(
this.data,
style: textStyle,
);
}
}
Use the extension method on the Text widget:
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
...List.generate(
manufacturers.length,
// call the applyStyle method on the Text widget
(index) => manufacturers[index].applyStyle(kStyle),
).toList(),
],
),
);

Search bar with ListView possible in Flutter?

I want to implement a searchbar in my flutter application. I have to go through a listview out of ListTiles. Here I want to check if the title of the listtile contains the letters in the search field. Is this possible with a List?
It does not have to be with the title. It could be something else with what I can identify the Tile. But please, not the index, the user would not know it.
Is a List the right widget or do I have to use something else to implement a search Engine in my Application
Rather than using a 3rd party package, you can use native showSearch() function :
showSearch(context: context, delegate: ListSearchDelegate());
And then a class extending SearchDelegate:
class ListSearchDelegate extends SearchDelegate{
ListSearchDelegate({Key key,}): super() ;
List<String> listItems = <String>['One', 'Two', 'Three', 'Four', 'Five'] ;
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
List<String> subList ;
subList = query != '' ? listItems.where((item) => item.contains(query)).toList() :
listItems ;
return ListView.builder(
itemCount: subList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(subList[index]),
);
}
);
}
#override
Widget buildSuggestions(BuildContext context) {
return Container();
}
}
Try https://pub.dev/packages/flutter_search_panel
List<SearchItem<int>> data = [
SearchItem(0, 'This'),
SearchItem(1, 'is'),
SearchItem(2, 'a'),
SearchItem(3, 'test'),
SearchItem(4, '.'),
];
FlutterSearchPanel<int>(
padding: EdgeInsets.all(10.0),
selected: 3,
title: 'Demo Search Page',
data: data,
icon: new Icon(Icons.check_circle, color: Colors.white),
color: Colors.blue,
textStyle: new TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20.0, decorationStyle: TextDecorationStyle.dotted),
onChanged: (int value) {
print(value);
},
),

pass a List to a QR generator

I am new to programming with flutter and Im trying to create a pizza app. first you create the pizza, choosing the ingredients, then you get a list of your order. The list works and the qr generator as well but they are from separate tutorials and I am having issues connecting them. I cant seem to pass the order list to the generator.
import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
import 'dart:io';
import 'package:flutter/rendering.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pizza_app/screens/review.dart';
import 'package:pizza_app/code/pizza.dart';
class GenerateQR extends StatefulWidget {
List<String> _list = new List<String>();
#override
State<StatefulWidget> createState() => GenerateQRState();
GenerateQR(list){
_list = list;
}
}
class GenerateQRState extends State<GenerateQR> {
static const double _topSectionTopPadding = 50.0;
static const double _topSectionBottomPadding = 20.0;
static const double _topSectionHeight = 50.0;
GlobalKey globalKey = new GlobalKey();
List<String> _dataList = new List<String>();
//String _dataString = "Hello from this QR";
String _inputErrorText;
final TextEditingController _textController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('QR Code Generator'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share),
onPressed: _captureAndSharePng,
)
],
),
body: _contentWidget(),
);
}
Future<void> _captureAndSharePng() async {
try {
RenderRepaintBoundary boundary = globalKey.currentContext.findRenderObject();
var image = await boundary.toImage();
ByteData byteData = await image.toByteData(format: ImageByteFormat.png);
Uint8List pngBytes = byteData.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final file = await new File('${tempDir.path}/image.png').create();
await file.writeAsBytes(pngBytes);
final channel = const MethodChannel('channel:me.alfian.share/share');
channel.invokeMethod('shareFile', 'image.png');
} catch(e) {
print(e.toString());
}
}
_contentWidget() {
final bodyHeight = MediaQuery.of(context).size.height - MediaQuery.of(context).viewInsets.bottom;
return Container(
color: const Color(0xFFFFFFFF),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
top: _topSectionTopPadding,
left: 20.0,
right: 10.0,
bottom: _topSectionBottomPadding,
),
child: Container(
height: _topSectionHeight,
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: TextField(
controller: _textController,
decoration: InputDecoration(
hintText: "Enter a custom message",
errorText: _inputErrorText,
),
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: FlatButton(
child: Text("SUBMIT"),
onPressed: () {
setState((){
_dataList.add(_list); // This must be wrong
_inputErrorText = null;
});
},
),
)
],
),
),
),
Expanded(
child: Center(
child: RepaintBoundary(
key: globalKey,
child: QrImage(
data: _dataList,// I think that there is an error here too
size: 0.5 * bodyHeight,
onError: (ex) {
print("[QR] ERROR - $ex");
setState((){
_inputErrorText = "Error! Maybe your input value is too long?";
});
},
),
),
),
),
],
),
);
}
}
Use Iterable.Join() to join your list of strings into a single string, using a suitable separator like the pipe (|) symbol:
Handle the case where somebody types the | character into the TextField.
The code that reads the QR code can use String.split() to split the string into a list of strings again.