A non-null String must be provided to a Text widget - flutter - flutter

I'm new to flutter and I'm learning it via a course on Udemy in this course we learn to create a simple BMI calculator which I did but the problem is that my app stats working perfectly and I can change the numbers and gender on it and sometimes it shows the result
<════════ Exception caught by widgets library ════════════
The following assertion was thrown building ResultsPage(dirty):
A non-null String must be provided to a Text widget.
'package:flutter/src/widgets/text.dart':
Failed assertion: line 381 pos 10: 'data != null'
The relevant error-causing widget was:
ResultsPage
file:///D:/FlutterProjects/bmi_calculator/lib/input_page.dart:223:47
When the exception was thrown, this was the stack:
#2 new Text (package:flutter/src/widgets/text.dart:381:10)
#3 ResultsPage.build (package:bmi_calculator/results_page.dart:50:19)
#4 StatelessElement.build (package:flutter/src/widgets/framework.dart:4749:28)
#5 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4675:15)
#6 Element.rebuild (package:flutter/src/widgets/framework.dart:4369:5)>
I don't know exactly what the problem is I tried to read similar problems here but unfortunately, I couldn't find which text widget I pass empty. this is the code for my results page
import 'package:bmi_calculator/bottom_button.dart';
import 'package:bmi_calculator/constants.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'reusable_card.dart';
class ResultsPage extends StatelessWidget {
ResultsPage(
{#required this.resultText,
#required this.bmiResult,
#required this.interpretation});
final String resultText;
final String bmiResult;
final String interpretation;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your Results'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
'Your Results',
style: kTitleStyle,
),
),
),
Expanded(
flex: 5,
child: ReusableCard(
colour: kActiveCardColour,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
bmiResult,
style: kResultTextStyle,
),
Text(
resultText,
style: kNumberTextStyle.copyWith(
fontSize: 100.0,
fontWeight: FontWeight.bold,
),
),
Text(
interpretation,
textAlign: TextAlign.center,
style: kBMIResultTextStyle,
)
],
),
),
),
BottomButton(
onTap: () => Navigator.pop(context), buttonTitle: 'ReCalculate')
],
),
);
}
}
EDIT:
Thanks, everyone for responding to my issue.
All of the solutions worked for me and stopped my app from crashing. and I used this method
Text(
bmiResult ?? '',
style: kResultTextStyle,
),
Text(
resultText ?? '',
style: kNumberTextStyle.copyWith(
fontSize: 100.0,
fontWeight: FontWeight.bold,
),
),
Text(
interpretation ?? '',
textAlign: TextAlign.center,
style: kBMIResultTextStyle,
)
to keep my code short and concise. But, I also realized something else in my code. I have three conditions in my app, which as you can guess are underweight, normal, and overweight. When I calculate the results, the application shows resultText and interpretation only if it's overweight. if the result is normal or underweight, it doesn't show them, and I assume it means this is what makes my app to crash because somehow it can't get the Text strings which I assigned for normal and underweight.

Either initialize the values to be not null or simply using ?? Operator to make this empty string when null is detected
children: <Widget>[
Text(
bmiResult ?? "",
style: kResultTextStyle,
),
Text(
resultText ?? "",
style: kNumberTextStyle.copyWith(
fontSize: 100.0,
fontWeight: FontWeight.bold,
),
),
Text(
interpretation ?? "",
textAlign: TextAlign.center,
style: kBMIResultTextStyle,
)
],

You just need to check if your variables : resultText, bmiResult, interpretation are not null because the Text widget cannot receive null value.
Text(
this.bmiResult != null ? this.bmiResult : "",
style: kResultTextStyle,
),
Text(
this.resultText != null ? this.resultText : "",
style: kNumberTextStyle.copyWith(
fontSize: 100.0,
fontWeight: FontWeight.bold,
),
),
Text(
this.interpretation != null ? this.interpretation : "",
textAlign: TextAlign.center,
style: kBMIResultTextStyle,
)
Or, you can just use the null-coalescing operator ?? to simplify :
this.interpretation ?? ""

You have at least 3 Text widgets where the data property is initialized from class members: resultText, bmiResult, interpretation. It looks like one of these member is null. To avoid the red screen you need to:
Call widget with initialized props:
ResultsPage(
resultText: 'Result Text',
bmiResult: 'BMI result',
interpretation: 'interpretation',
)
Set default values for ResultsPage's properties in constructor (e.g. empty string)
ResultsPage({this.resultText = '', this.bmiResult = '', this.interpretation = ''})
Use null aware feature in Text() widgets:
Text(resultText ?? 'n/a'),
This means that Text data will be equal to value of resultText if it is not null, or n/a if is null.
empty string will be used as data.

Related

flutter_markdown custom widget always on its own line

I'm using the flutter markdown package made by the flutter team here https://pub.dev/packages/flutter_markdown. I've created my own MarkdownElementBuilder based on their examples that inserts my own custom widget into the markdown and it looks like this:
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:outlit_app/constants/color_theme.dart';
import 'package:outlit_app/constants/dimension.dart';
import 'package:outlit_app/models/models.dart';
import 'package:markdown/markdown.dart' as md;
class DefinitionBuilder extends MarkdownElementBuilder {
final List<Definition> definitions;
DefinitionBuilder(this.definitions) : super();
#override
Widget visitElementAfter(md.Element element, TextStyle preferredStyle) {
final String textContent = element.textContent;
Definition definition = definitions.firstWhere(
(def) => textContent.toLowerCase().contains(def.word.toLowerCase()),
orElse: () =>
Definition(word: 'nothing found for $textContent', definition: ''),
);
return Tooltip(
margin: EdgeInsets.all(Dimensions.MARGIN_SIZE_EXTRA_LARGE),
padding: EdgeInsets.all(Dimensions.PADDING_SIZE_DEFAULT),
decoration: BoxDecoration(
color: GetColor.gradientPurple,
borderRadius: BorderRadius.circular(8),
),
verticalOffset: -10,
triggerMode: TooltipTriggerMode.tap,
message: definition.definition.trim(),
child: Text(
textContent.trim(),
style: TextStyle(
color: GetColor.primaryColor,
fontSize: Dimensions.FONT_SIZE_OVER_LARGE,
),
),
);
}
}
class DefinitionSyntax extends md.InlineSyntax {
static final String AST_SYMBOL = 'def';
DefinitionSyntax() : super(_pattern);
static const String _pattern = r'{{(.*)}}';
#override
bool onMatch(md.InlineParser parser, Match match) {
parser.addNode(md.Element.text(AST_SYMBOL, match[1]));
return true;
}
}
It works well but the widget is always on it's own seperate line as opposed to being inline with the rest of the text. If I return a simple text widget I still get the same thing.
Any tips in the right direction would be great :)
I got it work although not perfect because the leading distribution is a little off with the text of the tooltip but the widget that gets embedded now looks like this:
return RichText(
text: TextSpan(
children: [
WidgetSpan(
child: Container(
child: Tooltip(
margin: EdgeInsets.all(Dimensions.MARGIN_SIZE_EXTRA_LARGE),
padding: EdgeInsets.all(Dimensions.PADDING_SIZE_DEFAULT),
decoration: BoxDecoration(
color: GetColor.gradientPurple,
borderRadius: BorderRadius.circular(8),
),
verticalOffset: -10,
triggerMode: TooltipTriggerMode.tap,
message: definition.definition.trim(),
child: Text(
textContent.trim(),
style: TextStyle(
color: GetColor.primaryColor,
fontSize: Dimensions.FONT_SIZE_OVER_LARGE,
leadingDistribution: TextLeadingDistribution.even,
height: 1,
),
),
),
))
],
),
);

Can a single TextField in flutter have variable line height?

I'm implementing a simple rich text editor that renders text with a text editing controller that recognises basic markdown syntax, I'll link some code down below.
Everything works fine, the only problem I'm having is when a text style requires a bigger line height, for instance an # h1 that should be rendered as a title and therefore require a bigger line height overlaps over the previous line, as you can see in the screenshot below.
I've not been able so far to make the line height in a TextView variable based on the style of the text that is being displayed, is such thing even achievable in a Flutter TextView?
Here's a snippet of my text editing controller and a screenshot detailing my problem.
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class AddNotePage extends StatelessWidget {
final TextEditingController _controller = MarkdownTextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add Note'),
),
body: GestureDetector(
onVerticalDragDown: (_) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: TextField(
style: defaultTextStyle,
controller: _controller,
decoration: InputDecoration(
hintText: "Insert your message",
border: UnderlineInputBorder(
borderSide: BorderSide.none,
),
),
scrollPadding: EdgeInsets.all(20.0),
keyboardType: TextInputType.multiline,
maxLines: null,
),
),
],
),
),
);
}
}
const Map<String, TextStyle> defaultMarkdownStyleMap = {
r'^# .*?$': TextStyle(
fontWeight: FontWeight.bold,
fontSize: 50,
),
r'^## .*?$': TextStyle(
fontWeight: FontWeight.bold,
fontSize: 40,
),
r'^### .*?$': TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
r'__(.*?)\__': TextStyle(fontStyle: FontStyle.italic, fontSize: 20),
r'~~(.*?)~~': TextStyle(decoration: TextDecoration.lineThrough, fontSize: 20),
r'\*\*(.*?)\*\*': TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
};
const TextStyle defaultTextStyle = TextStyle(fontSize: 20);
class MarkdownTextEditingController extends TextEditingController {
final Map<String, TextStyle> styleMap;
final Pattern pattern;
MarkdownTextEditingController({this.styleMap = defaultMarkdownStyleMap})
: pattern = RegExp(
styleMap.keys.map((key) {
return key;
}).join('|'),
multiLine: true);
#override
TextSpan buildTextSpan(
{required BuildContext context,
TextStyle? style,
required bool withComposing}) {
final List<InlineSpan> children = [];
text.splitMapJoin(
pattern,
onMatch: (Match match) {
TextStyle? markdownStyle = styleMap[styleMap.keys.firstWhere(
(e) {
return RegExp(e).hasMatch(match[0]!);
},
)];
children.add(TextSpan(
text: match[0],
style: style!.merge(markdownStyle),
));
return "";
},
onNonMatch: (String text) {
children
.add(TextSpan(text: text, style: style!.merge(defaultTextStyle)));
return "";
},
);
return TextSpan(style: style, children: children);
}
}
I've found a solution.
All I needed to do was to play around with the strutStyle property of the TextField.
As the documentation states:
The strut style used for the vertical layout.
StrutStyle is used to establish a predictable vertical layout. Since
fonts may vary depending on user input and due to font fallback,
StrutStyle.forceStrutHeight is enabled by default to lock all lines to
the height of the base TextStyle, provided by style. This ensures the
typed text fits within the allotted space.

I am trying to pass may multiple data on screen to another screen in flutter but it shows the Invalid argument error

Hello I am trying to pass may multiple data on one screen to another screen in flutter but it shows the Invalid argument error. I couldn't recognize where hav the error.I provide my navigation part code and antoher activity part code.
=>Home Activity navigation part method.
-this is the navigate method.
getItemAndNavigation(BuildContext context){
Navigator.push(context, MaterialPageRoute(builder: (context)=>resultScanner(
scanResult: scannedResult,
resultType: resultType,
)));
}
=> this is my second activity code.
class resultScanner extends StatelessWidget {
final scanResult;
final resultType;
resultScanner({Key key, #required this.scanResult, this.resultType})
: super(key: key);
String currentTime = '';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
iconTheme: IconThemeData(
color: Colors.black,
),
title: Text(
"Result",
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Result type = ' + resultType,
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
Text(
'Description = ' + scanResult,
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
],
)),
);
}
}
=>This is the erros showing.
> The following ArgumentError was thrown building resultScanner(dirty,
> state: _resultScannerState#7c9c8): Invalid argument(s)
>
> The relevant error-causing widget was: resultScanner
> file:///F:/Work/QReader/qreader/qreader/lib/screens/homeui.dart:458:34
> When the exception was thrown, this was the stack:
> #0 _StringBase.+ (dart:core-patch/string_patch.dart:267:57)
> #1 _resultScannerState.build (file:///F:/Work/QReader/qreader/qreader/lib/screens/result_scan.dart:46:30)
> #2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4663:28)
> #3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4546:15)
> #4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4719:11)
This is because you have not defined the type, on the second screen.
class resultScanner extends StatelessWidget {
final scanResult;
final resultType;
}
Change it to final String scanResult and final String resultType
Or Maybe whatever you want them to be.
There is another problem with this code in the print Statement:
Text(
'Result type = ' + resulType,
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
It should be:
Text(
'Result type = $resultType'
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
Same goes for scanResult.
I'm a little surprised it compiles at all, but you did not give your variables any type.
So this line is basically having some religious faith in the fact that the variables passed are something that can be concatenated with a plus symbol:
'Result type = ' + resultType,
Give your two variables scanResult and resultType a type (maybe they are strings?) and then figure out whether a simple plus is the right way to concatenate them with another string.

Flutter: Show data from RestAPI

I am trying to show the data from database to Flutter.
I am able to get the data but don't know how can i show it in Flutter. Single data i can show but getting hard time presenting multiple data.
I am using DIO plugin for HTTP requests.
Here is the code.
Future getData() async{
try{
dtguid ='34';
var bodyss = { "uid" : dtgUid, "deviceid": deviceid};
Response<Map> responsess =
await Dio().post("http://192.168.100.4:8080/sampleapp/get-followingdata.php", data: bodyss,);
Map responseBody = response.data;
if(responseBodys['success'] == false){
_showSnackBar(context,responseBody['errors']['inputuid'],Colors.redAccent);
this.setState((){
_inProcess = false;
});
}else{
print(responseBody['success']);
totalcount = responseBody['count'];
this.setState((){
_inProcess = false;
});
}
}catch(e){
print("Exception Caught: $e");
}
}
Here is the Widget where i need to show this data.
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Test',// schoolLists[index]['name'],
style: TextStyle(
color: primary,
fontWeight: FontWeight.bold,
fontSize: 18),
),
SizedBox(
height: 6,
),
Row(
children: <Widget>[
Icon(
Icons.location_on,
color: secondary,
size: 20,
),
SizedBox(
width: 5,
),
Text(
'Earth',//schoolLists[index]['location'],
style: TextStyle(
color: primary, fontSize: 13, letterSpacing: .3)),
],
),
SizedBox(
height: 6,
),
Row(
children: <Widget>[
Icon(
Icons.school,
color: secondary,
size: 20,
),
SizedBox(
width: 5,
),
Text(
'Some Data',
//schoolLists[index]['type'],
style: TextStyle(
color: primary, fontSize: 13, letterSpacing: .3)),
],
),
],
),
)
For testing purpose i tried with some hardcoded data schoolLists that is working but i don't know how can i show the data from http request.
Sample Data.
{"errors":[],
"content":[{"uid":34,"age":35,"name":"Test User 1","country":"India"},
{"uid":34,"age":37,"name":"Test User 2","country":"India"},
{"uid":34,"age":36,"name":"Test User 3","country":"India"}],
"success":true}
I need to show name and country to the widget.
In place of this test value.
Text(
'Test',// schoolLists[index]['name'],
style: TextStyle(
color: primary,
fontWeight: FontWeight.bold,
fontSize: 18),
),
Later on i will try to work on Lazyload. Unfortunatly, i am not able to show the data so, didn't asked about the lazyload.
To be honest i am learning Flutter. I don't have much experience in it.
i dont see you decoding the body response data
on top of your code first import
import 'dart:convert' as convert;
than in your function getData() decode the server JSON response like this
//get the json data decode it and store it in decodedResponse
var decodedResponse = convert.jsonDecode(response.body);
than map thru your decodedResponse as you want

text with \n and unicode literals saved in mysql do not work when displayed

I store a text string with \n and unicode literals like \u2022 in mysql, then retrieve it with http api call on flutter. When displaying it with Text widget, these escaped symbles do not show as expected. When I directly pass the string , it works. Could anyone help me out?
child: Column(
children: <Widget>[
Text(prompt.prompt_body, //This variable is from http call which does not work
textAlign: TextAlign.left,
style:TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic
)),
Divider(),
Text("You live in a room in college which you share with another student.However, there are many problems with this arrangement and you find it very difficult to work.\n\nWrite a letter to the accommodation officer at the college. In the letter,\n\n \u2022 describe the situation\n \u2022 explain your problems and why it is difficult to work\n \u2022 say what kind of accommodation you would prefer", //this part works
textAlign: TextAlign.left,
style:TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic
))
],
),
emulator screenshot
In response to Gunter's query, I add the following code on api call:
class PromptModel {
int id;
String prompt_body;
String prompt_image;
PromptModel(this.id, this.prompt_body, this.prompt_image);
PromptModel.fromJson(Map<String, dynamic> parsedJson) {
id = parsedJson['id'];
prompt_body = parsedJson['prompt_body'];
prompt_image = parsedJson['prompt_image'];
}
}
....
class PromptListPageState extends State<PromptListPage> {
int counter = 0;
List<PromptModel> prompts = [];
void fetchImage() async {
counter++;
var response =
await get('http://10.0.2.2:8080/TestPrompt');
var promptModel = PromptModel.fromJson(json.decode(response.body));
setState(() {
prompts.add(promptModel);
});
}
The following is the response of the api call:
{"id":1,"prompt_body":"You live in a room in college which you share with another student.However, there are many problems with this arrangement and you find it very difficult to work.\\n\\nWrite a letter to the accommodation officer at the college. In the letter,\\n\\n \\u2022 describe the situation\\n \\u2022 explain your problems and why it is difficult to work\\n \\u2022 say what kind of accommodation you would prefer","prompt_image":"http://10.0.2.2:8080/test.jpg"}
I solved the problem by inputting the string from flutter using TextFormField. directly inserting the text on database side is tricky. The code is as below:
Widget build(context) {
return MaterialApp(
home: Scaffold(
body: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
controller: myController,
maxLines: 5,
validator: (val) =>
(val == null || val.isEmpty) ? "请输入商品名称" : null,
decoration: const InputDecoration(
//icon: Icon(Icons.person),
hintText: 'add the prompt here:',
labelText: 'Prompt content',
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.teal)),
),
onSaved: (val) => this.content = val,
),
new Container(
margin: const EdgeInsets.only(top: 10.0),
child: new RaisedButton(
onPressed: _save,
child: new Text('Save'),
),
)
]),
),
appBar: AppBar(
title: Text('Add Essay Prompt'),
),
),
);
}
}