flutter - richtext, textspan from a list - flutter

I don't have much experience with flutter.
I would like to use the language_tool library (https://pub.dev/packages/language_tool) for Dart and Flutter.
I'm trying to create a text where the errors found by the tool() function are clickable.
To do this I thought of using the RichText widget with TextSpan as described here: Make specific parts of a text clickable in flutter
For now I have written this code, but I don't know how to go on
import 'package:flutter/material.dart';
import 'package:language_tool/language_tool.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele';
List<WritingMistake> mistakes = [];
List<TextSpan> richtext = [];
void tool(String text) async {
var tool = LanguageTool();
var result = tool.check(text);
var correction = await result;
for (var m in correction) {
WritingMistake mistake = WritingMistake(
message: m.message,
offset: m.offset,
length: m.length,
issueType: m.issueType,
issueDescription: m.issueDescription,
replacements: m.replacements,
);
mistakes.add(mistake);
}
print(mistakes.length);
print(mistakes);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
Container(
color: Colors.blue,
height: 150.0,
width: double.infinity,
child: Center(
child: Container(
child: RichText(
text: TextSpan(
text: '',
style: TextStyle(color: Colors.black),
children: richtext,
),
),
),
),
),
],
),
),
);
}
}
I hope I was clear and that someone can help me.
Thank you :)

Here is a small example of what your clickable words should look like in the content. Copy and paste into DartPad to play with it.
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
List<String> mistakes = ['bugs', 'error'];
String content = 'Your application status is error free'
' and no bugs were found.';
Future<void> onTap(String text) async {
print(text);
}
#override
Widget build(BuildContext context) {
String text = 'Henlo i am Gabriele';
return Scaffold(
body: SafeArea(
child: ListView(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0)),
),
),
Container(
color: Colors.blue,
height: 150.0,
width: double.infinity,
child: Center(
child: RichText(
text: TextSpan(
text: 'App status: ',
style: const TextStyle(color: Colors.black),
children: [
for (String word in content.split(' '))
if (mistakes.contains(word)) ...[
TextSpan(
text: word + ' ',
style: const TextStyle(fontWeight: FontWeight.bold),
recognizer: TapGestureRecognizer()
..onTap = () {
// Do anything with the state on click.
onTap(word);
},
)
] else ...[
TextSpan(text: word + ' ')
],
],
),
),
),
),
],
),
),
);
}
}

Related

setState() or markNeedsBuild() called during build. This QuoteList widget cannot be marked as needing to build because the framework in the process

i am learning flutter and i am taking demo in which a guy is calling a fucnction on button pressed. but i am getting an error : setState() or markNeedsBuild() called during build
please guide me what to do
below is my code
QuoteList.dart
import 'dart:ffi';
import 'quote.dart';
import 'package:flutter/material.dart';
import 'QuoteCard.dart';
void main() {
runApp(MaterialApp(home: QuoteList()));
}
class QuoteList extends StatefulWidget {
const QuoteList({Key? key}) : super(key: key);
#override
State<QuoteList> createState() => _QuoteListState();
}
class _QuoteListState extends State<QuoteList> {
List<Quote> quotes = [
Quote('Be yourself, Everyone else is already taken.','sana'),
Quote('I have nothing to declare except my genius','sana'),
Quote('The truth is rarely pure and never simple.', 'sana'),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
title: Text(
"Awesome Quotes",
),
centerTitle: true,
backgroundColor: Colors.red,
),
body: Column(
children: quotes.map((quote) => QuoteCard(
quote: quote,
delete: () {
setState(() {
quotes.remove(quote);
});
}
)).toList()
)
);
}
}
and below is QuoteCard widget code. QuoteCard is a widget in seperate class and is called by main.dart file. The code is below for the quote card:
import 'dart:ffi';
import 'quote.dart';
import 'package:flutter/material.dart';
class QuoteCard extends StatelessWidget {
final Quote quote;
final Function delete;
QuoteCard( {required this.quote, required this.delete });
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.fromLTRB(16, 16, 16, 0),
color: Colors.grey[100],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
quote.text,
style: TextStyle(
fontSize: 18,
color: Colors.grey[600],
),
),
SizedBox(height: 6.0),
Text (
quote.author,
style: TextStyle(
fontSize: 18,
color: Colors.grey[600],
),
),
SizedBox(height: 6.0),
FlatButton.icon(
onPressed: delete(),
icon: Icon(Icons.delete),
label: Text('delete')
)
]
),
),
);
}
}
It's coming from
onPressed: delete(),
which call the function every time the widget is built.
Try this instead
onPressed: delete,

Flutter - Row added -> change the text of a container

I'm quite inexperienced with flutter and have created this script.
When you tap on the red container you create a Row of buttons,
I would like when I click on a button in the Row -> the text of the blue container becomes the same as the text contained in the tapped button
Anyone know how I can do?
Thank you :)
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele!';
List<Container> OutputList = [];
void tool(String text) async {
List ListText = text.split(' ');
for (var i in ListText) {
OutputList.add(
Container(
child: GestureDetector(
onTap: () {},
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
color: Colors.orange,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(i),
),
),
),
),
),
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
GestureDetector(
onTap: () {
setState(() {
tool(text);
print(OutputList);
});
},
child: Container(
width: 150.0,
height: 50.0,
color: Colors.red,
child: Center(child: Text('START ->')),
),
),
SizedBox(height: 50.0),
Row(
children: OutputList,
),
SizedBox(height: 50.0),
Container(
color: Colors.blue,
width: 200.0,
height: 50.0,
child: Text(''),
),
],
),
),
);
}
}
Yes you can add a few line of code check here i try to solve.
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele!';
//step 1 create variable
String newGeneratedText = "";
List<Container> OutputList = [];
void tool(String text) async {
List ListText = text.split(' ');
for (var i in ListText) {
OutputList.add(
Container(
child: GestureDetector(
onTap: () {
//add logic here to concatinate values
setState(() {
newGeneratedText = newGeneratedText + " " + i;//added " " for one space
});
},
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
color: Colors.orange,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(i),
),
),
),
),
),
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
GestureDetector(
onTap: () {
setState(() {
tool(text);
print(OutputList);
});
},
child: Container(
width: 150.0,
height: 50.0,
color: Colors.red,
child: Center(child: Text('START ->')),
),
),
SizedBox(height: 50.0),
Wrap( // added for fixing more values and solve overflow exceptions error
children: OutputList,
),
SizedBox(height: 50.0),
Container(
color: Colors.blue,
width: 200.0,
height: 50.0,
child: Text(newGeneratedText), //final print values
),
],
),
),
);
}
}

Why can I not assign type 'Translation' to type 'String' using translator plugin?

I am building a translation app and am having difficulties with assigning the final translation to a variable storing that translation, because I want to use it in another place. Code:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:translator/translator.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Translator',
home: MyHomePage(title: 'Translator'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController textController = TextEditingController();
var translatedPhrase = "";
var translator = GoogleTranslator();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold (
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.green[100],
title: const Text(
"What Are You Saying In Spanish?",
style: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontWeight: FontWeight.w600,
),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
flex: 1,
child: Container(
width: double.infinity,
height: double.infinity,
color: Colors.lightBlue,
child: Column(
children: <Widget>[
TextField(
controller: textController,
),
MaterialButton(
child: const Text("Translate"),
color: Colors.white,
onPressed: () {
setState(() {
// ignore: non_constant_identifier_names
translator.translate(textController.text, from: "en", to: "es").then((t) {
setState(() {
translatedPhrase = t;
});
});
});
},
),
],
),
),
),
Flexible(
flex: 1,
child: Container(
width: double.infinity,
height: double.infinity,
color: Colors.grey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
translatedPhrase,
style: const TextStyle(
fontSize: 20,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget> [
MaterialButton(
child: const Icon(Icons.clear),
onPressed: () {
setState(() {
translatedPhrase = "";
textController.text = "";
});
},
),
MaterialButton(
child: const Icon(Icons.content_copy),
onPressed: () {
Clipboard.setData(ClipboardData(text: translatedPhrase));
},
),
],
)
],
),
),
),
],
),
),
),
);
}
}
I am getting the error "A value of type 'Translation' can't be assigned to a variable of type 'String'. Try changing the type of the variable, or casting the right-hand type to 'String'." on line 77 (translatedPhrase = t;). basically, I just would like some help/advice on how to get the Materialbutton working to do the translation function. Thank you!
The translate method returns a Translation Object so You can not assign it to a String. The Translation object has a property text.
So your code should look like this:
translator.translate(textController.text, from: "en", to: "es").then((t) {
setState(() {
translatedPhrase = t.text;
});
});
Try with the following code, hope you got the solution.
GoogleTranslator translator = GoogleTranslator();
await translator
.translate(textController.text, from: 'en', to: 'es')
.then((value) {
translatedPhrase = value.toString();
setState(() {});
});

How to color the suggestions when the user is typing in flutter

I am using typeahead package in flutter to get suggestions to users as they type, what I want is to color the suggestions while the user is typing as its shouwn in the picture Colored suggestions while typing
Here is a simple example that am trying to implement
Main.dart
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'CitiesService.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Colored suggestions Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Colored suggestions Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final myControllerCity = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Form(
key: this._formKey,
child: Padding(
padding: EdgeInsets.all(32.0),
child: Column(
children: <Widget>[
//SizedBox(height: 50,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: TypeAheadFormField(
textFieldConfiguration: TextFieldConfiguration(
controller: myControllerCity,
decoration: InputDecoration(labelText: 'City')),
suggestionsCallback: (pattern) {
return CitiesService.getSuggestionsCities(pattern);
},
itemBuilder: (context, suggestion) {
//
List<String> splitted_names_of_cities = suggestion
.toString()
.toLowerCase()
.split(myControllerCity.text);
final children = <Widget>[];
for (var i = 1;
i < splitted_names_of_cities.length;
i++) {
children.add(new ListTile(
title: Text.rich(
TextSpan(
children: [
TextSpan(
text: myControllerCity.text,
style: TextStyle(color: Colors.red),
),
TextSpan(text: splitted_names_of_cities[i]),
],
),
)));
}
print("this is the list $splitted_names_of_cities");
return new ListView(
children: children,
);
},
transitionBuilder: (context, suggestionsBox, controller) {
return suggestionsBox;
},
onSuggestionSelected: (suggestion) {
myControllerCity.text = suggestion;
},
),
),
],
),
],
),
),
)),
);
}
}
And here is the function of the suggestions
static List<String> getSuggestionsCities(String query) {
List<String> wilayas = [];
algeria_cites.forEach((element) => wilayas.contains(element['wilaya_name_ascii']) ?
null : wilayas.add(element['wilaya_name_ascii']) );
wilayas.retainWhere((s) => s.toLowerCase().contains(query.toLowerCase()));
return wilayas;
}
In the previous code, for each item of the suggested cities name I was creating a full ListTile but I had to create TextSpan instead of that.
But each item has its specific length, for that reason I used List<InlineSpan>.
So here is the itemBuilder after fixing the error.
itemBuilder: (context, suggestion) {
List<InlineSpan> temp = [];
String suggestion_in_lower_case = suggestion.toString().toLowerCase();
List<String> splitted_names_of_cities = suggestion_in_lower_case.split(myControllerCity.text.toLowerCase());
for (var i = 0; i < splitted_names_of_cities.length-1; i++) {
if(splitted_names_of_cities.contains(myControllerCity.text.toLowerCase()));{
temp.add(
TextSpan(
text: splitted_names_of_cities[i],
style: TextStyle(
height: 1.0,
color: Colors.black,
),
),
);
temp.add(
TextSpan(
text: myControllerCity.text.toLowerCase(),
style: TextStyle(
color: Colors.red,
),
),
);
}
}
temp.add(
TextSpan(
text: splitted_names_of_cities.last,
style: TextStyle(
color: Colors.black,
),
),
);
return ListTile(
title: Text.rich(
TextSpan(
children: temp,
),
)
);
},
So it is working as expected, here is the demo.
https://i.stack.imgur.com/Tfs95.gif

Flutter - How do I create this Signup Form?

I'm trying to place an input form field underneath my text which says please enter your email. Could anyone assist? The thing I'm having the biggest problem with right now is I don't know how to add anything else below the text. Ideally, I'd like a centred input field. Code is below:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:google_fonts/google_fonts.dart';
void main() {
runApp(MyApp());
}
class MyCustomForm extends StatefulWidget {
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
class MyCustomFormState extends State<MyCustomForm> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Enter your email'),
),
]),
);
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test Bench',
home: Stack(children: [
new Scaffold(
body: new Container(
decoration: BoxDecoration(color: Colors.pinkAccent),
child: Padding(
padding: const EdgeInsets.all(30.0),
child: Text(
"Hello, Let's Get Started...",
style: TextStyle(
fontSize: 60.0,
fontWeight: FontWeight.bold,
fontFamily: 'Oswald',
color: Colors.black),
),
),
),
),
]));
}
}
You can wrap the text with a Column to append widgets underneath each other (Video). You can view more information Here. This video also helps with general layouts (link).
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyCustomForm extends StatefulWidget {
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
class MyCustomFormState extends State<MyCustomForm> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Enter your email'),
),
]),
);
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test Bench',
home: Stack(
children: [
Scaffold(
body: Container(
width: double.infinity,
decoration: BoxDecoration(color: Colors.pinkAccent),
child: Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Hello, Let's Get Started...",
style: TextStyle(
fontSize: 60.0,
fontWeight: FontWeight.bold,
fontFamily: 'Oswald',
color: Colors.black,
),
),
MyCustomForm(),
],
),
),
),
),
],
),
);
}
}
You probably have trouble to find how to add multiples widgets as the child of your Scaffold.
I suggest you to check out the official documentation on layouts
You'll learn there how to create more complex layout and how you can add an input text field under a text widget.