Why it is showing "The getter 'exists' was called on null"? - android

I'm trying to make a favorite button in my flutter app with Firebase. But when I use snapshot.hasData to see if the particular item is already present in favorite list, it always returns true, even if the item is not present in the database. So I tried snapshot.data.exists and it works. But, eventhough the app is working fine", it always shows following error in the debug console:
The getter 'exists' was called on null.
Receiver: null
Tried calling: exists
My Full code:
Widget build(BuildContext context) {
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection("UserData")
.doc(_auth.currentUser.uid)
.collection("Favourites")
.doc(widget.items["name"])
.snapshots(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
return Scaffold(
body: Row(
children: [
snapshot.data.exists
? Expanded(
child: TextButton.icon(
onPressed: () {
FirebaseFirestore.instance
.collection("UserData")
.doc(_auth.currentUser.uid)
.collection("Favourites")
.doc(widget.items["name"])
.delete();
},
label: Text(
"Unfavourite Item",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).accentColor),
),
icon: Icon(
Icons.star,
color: Theme.of(context).accentColor,
),
style: TextButton.styleFrom(
minimumSize: Size.fromHeight(50),
elevation: 0),
),
)
: Expanded(
child: TextButton.icon(
onPressed: () {
FirebaseFirestore.instance
.collection("UserData")
.doc(_auth.currentUser.uid)
.collection("Favourites")
.doc(widget.items["name"])
.set({
"name": widget.items["name"],
"image": widget.items["image"],
"price": widget.items["price"],
"locate": widget.items["locate"],
"assorted": true
});
},
label: Text(
"Favourite Item",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).accentColor),
),
icon: Icon(
Icons.star_border,
color: Theme.of(context).accentColor,
),
style: TextButton.styleFrom(
minimumSize: Size.fromHeight(50),
elevation: 0),
)),
],
),
);
}
);
}
Please help. I'm new to flutter and firebase.

Your snapshot is null at that time. So handle it like
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done){
return Scaffold(
body: Row(
children: [
snapshot.data.exists
? Expanded(
child: TextButton.icon(
onPressed: () {
FirebaseFirestore.instance
.collection("UserData")
.doc(_auth.currentUser.uid)
.collection("Favourites")
.doc(widget.items["name"])
.delete();
},
label: Text(
"Unfavourite Item",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).accentColor),
),
icon: Icon(
Icons.star,
color: Theme.of(context).accentColor,
),
style: TextButton.styleFrom(
minimumSize: Size.fromHeight(50), elevation: 0),
),
)
: Expanded(
child: TextButton.icon(
onPressed: () {
FirebaseFirestore.instance
.collection("UserData")
.doc(_auth.currentUser.uid)
.collection("Favourites")
.doc(widget.items["name"])
.set({
"name": widget.items["name"],
"image": widget.items["image"],
"price": widget.items["price"],
"locate": widget.items["locate"],
"assorted": true
});
},
label: Text(
"Favourite Item",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).accentColor),
),
icon: Icon(
Icons.star_border,
color: Theme.of(context).accentColor,
),
style: TextButton.styleFrom(
minimumSize: Size.fromHeight(50), elevation: 0),
)),
],
),
);
}else{
return SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: const Center(
child: CircularProgressIndicator(),
),
);
}
}

Related

How to prevent TextFormField redirecting to previous screen?

I am trying to create form.
I managed to create every widget in it, but every time I try to open TextFormField I get redirected back to my MainMenuScreen without any error.
I am using BLoC and routes. I think that issue might be related with using named routes.
Issue was not spotted before changing to named routes
MainMenuScreen fragment:
CategoryCard(
categoryName: 'Main dishes',
assetPath: 'assets/images/main_dish.png',
onPressed: () => Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) {
return BlocProvider.value(
value: BlocProvider.of<RecipesBloc>(context)
..add(LoadRecipesEvent())
..category = 'main_dish',
child: RecipesScreen(),
);
})),
),
From MainMenuScreen I redirect to RecipesScreen
Fragment of RecipesScreen with redirect to RecipeCreateForm:
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (BuildContext context) {
return RecipeCreateForm();
}),
),
and then I redirect to RecipeCreateForm where I'm using TextFormFields.
Whenever I try to use TextFormField I get redirected back to MainMenuScreen.
class RecipeCreateForm extends StatefulWidget {
#override
_RecipeCreateFormState createState() => _RecipeCreateFormState();
}
class _RecipeCreateFormState extends State<RecipeCreateForm> {
final _recipeNameController = TextEditingController();
final _imageUrl = TextEditingController();
String? _difficultyValue;
late int _ingredientsQuantity;
late int _preparationStepsQuantity;
late List<Ingredient> _ingredientsValues;
late List<PreparationStep> _preparationStepsValues;
late double _preparationTime;
String? _portions;
#override
void initState() {
_ingredientsQuantity = 1;
_preparationStepsQuantity = 1;
_ingredientsValues = [];
_preparationStepsValues = [];
_preparationTime = 0;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
leading: IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(
Icons.arrow_back,
color: Colors.white,
),
),
),
body: Scrollbar(
thickness: 10,
hoverThickness: 2,
child: SingleChildScrollView(
child: Container(
color: Colors.lightGreen.shade100,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Recipe name',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
TextFormField(
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
controller: _recipeNameController,
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Image',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
TextFormField(
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
controller: _imageUrl,
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Difficulty',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
DropdownButton(
hint: _difficultyValue == null
? Text(
'Select difficulty',
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
)
: Text(
_difficultyValue!,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
items: ['Easy', 'Medium', 'Hard'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
_difficultyValue = val as String;
},
);
},
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Preparation time',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Slider(
value: _preparationTime,
onChanged: (newPreparationTime) {
setState(() => _preparationTime = newPreparationTime);
},
label: _preparationTime.toStringAsFixed(0),
min: 0,
max: 360,
divisions: 24,
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Ingredients',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
SizedBox(
height: 175,
child: Scrollbar(
child: ListView.builder(
itemCount: _ingredientsQuantity,
itemBuilder: (context, index) {
return _ingredientRow(index);
}),
),
),
Row(
children: [
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
setState(() {
_ingredientsQuantity++;
});
}),
IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
setState(() {
_ingredientsQuantity = 1;
_ingredientsValues.clear();
});
})
],
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Preparation steps',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Scrollbar(
child: SizedBox(
height: 100,
child: ListView.builder(
shrinkWrap: true,
itemCount: _preparationStepsQuantity,
itemBuilder: (context, index) {
return _preparationStepRow(index);
}),
),
),
Row(
children: [
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
setState(() {
_preparationStepsQuantity++;
});
}),
IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
setState(() {
_preparationStepsQuantity = 1;
_preparationStepsValues.clear();
});
}),
],
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Portions',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
DropdownButton(
hint: _portions == null
? Text(
'Select number of portions',
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
)
: Text(
_portions!,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
items: ['1', '2', '3', '4', '5', '6', '7'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
_portions = val as String;
},
);
},
),
ElevatedButton(
onPressed: () {
BlocProvider.of<RecipesBloc>(context).add(
AddRecipeEvent(
Recipe(
name: _recipeNameController.text,
image:
'https://www.thespruceeats.com/thmb/dA8o8EZpjJyeocYZNpzfknoKh2s=/4351x3263/smart/filters:no_upscale()/baked-stuffed-potatoes-482217-hero-01-850f2d87fe80403f923e140dbf5f1bf3.jpg',
ingredients: _ingredientsValues,
difficulty: _difficultyValue,
preparationTime: _preparationTime,
preparationSteps: _preparationStepsValues,
type: BlocProvider.of<RecipesBloc>(context)
.category
.toString(),
portions: _portions,
),
),
);
Navigator.of(context).pop();
},
child: Text('Submit'),
),
],
),
),
),
),
);
}
_ingredientRow(int key) {
return IntrinsicHeight(
child: Row(
children: [
Padding(padding: EdgeInsets.only(left: 10)),
SizedBox(
width: 225,
child: TextFormField(
maxLength: 35,
onChanged: (val) {
setState(() {
_onIngredientUpdate(key,name: val);
});
},
),
),
VerticalDivider(
width: 20,
thickness: 1,
color: Colors.black,
indent: 30,
endIndent: 10,
),
SizedBox(
width: 55,
child: TextFormField(
maxLength: 7,
initialValue: '0',
onChanged: (val) {
setState(() {
_onIngredientUpdate(key, quantity: val);
});
},
),
),
Padding(padding: EdgeInsets.only(left: 10)),
DropdownButton(
hint: Text('pcs'),
items: ['pcs', 'ml', 'g'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(() {
_onIngredientUpdate(key,measurement: val.toString());
});
},
)
],
),
);
}
_onIngredientUpdate(int key, {String? name, String? measurement, String? quantity}) {
int foundKey = -1;
_ingredientsValues.forEach((element) {
if (element.id.contains(key.toString())) {
foundKey = key;
}
});
if (-1 != foundKey) {
_ingredientsValues.removeWhere((map) {
return map.id == foundKey.toString();
});
}
Map<String, dynamic> json = {'id': key, 'name': name, 'measurement': measurement, 'quantity':quantity};
_ingredientsValues.add(json as Ingredient);
}
_preparationStepRow(int key) {
return IntrinsicHeight(
child: Row(
children: [
Padding(padding: EdgeInsets.only(left: 10)),
SizedBox(
width: 225,
height: 50,
child: TextFormField(
maxLength: 35,
onChanged: (val) => {
_onPreparationUpdate(key,val)
},
),
),
],
),
);
}
_onPreparationUpdate(int key, String val) {
int foundKey = -1;
_preparationStepsValues.forEach((element) {
if (element.id.contains(key.toString())) {
foundKey = key;
}
});
if (-1 != foundKey) {
_preparationStepsValues.removeWhere((map) {
return map.id == foundKey.toString();
});
}
Map<String, dynamic> json = {'id': key, 'step': val};
_preparationStepsValues.add(json as PreparationStep);
}
}
Issue GIF:
EDIT:
Issue is not related with form. I have replaced whole form with only one field without any logic and issue remains.
It is probably related to named routes.
As I was thinking, issue was related with usage of named routes.
I managed to bypass this issue with using Future.delayed and pushNamedAndRemoveUntil
In main_menu_screen I have created method which I later used to redirect to categories.
void redirectToCategory(BuildContext context, String categoryName) {
Future.delayed(Duration.zero, () {
Navigator.pushNamedAndRemoveUntil(
context,
'/recipeScreen',
(_) => false,
arguments: BlocProvider.value(
value: BlocProvider.of<RecipesBloc>(context)
..add(LoadRecipesEvent())
..category = categoryName,
child: RecipesScreen(),
),
);
});

Row widget contents are not getting displayed

I am trying to build an app which retrieves all the table data and display it on the screen.I have made it using a list builder widget .I wanted to add a header to each of the rows retrieved .I tried adding a row widget which contains the header.But it is not getting displayed .
Here is what the out List looks like
I wanted add did dname and age Header by making a row ..but its not visible
Here is what my code looks like
// build list view & its tile
ListView _buildPosts(BuildContext context, List<Post> posts) {
print('Hi');
Row(//This row is not getting displayed
children: [
Container(
child: const Text('Did',
style: TextStyle(
color: Colors.white,
),
),
),
Container(
child: const Text('Dname',style: TextStyle(
color: Colors.white,
),),
),
Container(
child: const Text('Age',style: TextStyle(
color: Colors.white,
),),
),
],
);
return ListView.builder(
itemCount: posts.length,
padding: const EdgeInsets.all(20),
itemBuilder: (context, index) {
return Card(
shadowColor: Colors.white,
color:const Color(0xFF303030),
elevation: 1,
child: IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(posts[index].Did,style: const TextStyle(fontWeight: FontWeight.bold,
color:Colors.white,),),
Text(posts[index].Dname,style: const TextStyle(fontWeight: FontWeight.bold,
color:Colors.white,),),
Text(posts[index].Age,style: const TextStyle(fontWeight: FontWeight.bold,color:Colors.white,),
),
],
),
),
);
},
);
}
}
class HttpService {
Future<List<Post>> getPosts() async {
Response res = await get(
Uri.parse('http://localhost/localconnect/driver_change.php'));
print(res.body);
if (res.statusCode == 200) {
List<dynamic> body = jsonDecode(res.body);
List<Post> posts = body.map(
(dynamic item) => Post.fromJson(item),
).toList();
return posts;
} else {
throw "Unable to retrieve posts.";
}
}
}
The problem is that the row isn't used anywhere. So instead of using this so called _buildPosts you can simply return a listView.builder. Checkout the code below:
I have added 2 sets of solutions below:
ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
return Card(
shadowColor: Colors.white,
color: const Color(0xFF303030),
elevation: 1,
child: IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Container(
child: const Text(
'Did',
style: TextStyle(
color: Colors.white,
),
),
),
Text(
posts[index].Did,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
Column(
children: [
Container(
child: const Text(
'Dname',
style: TextStyle(
color: Colors.white,
),
),
),
Text(
posts[index].Dname,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
Column(
children: [
Container(
child: const Text(
'Age',
style: TextStyle(
color: Colors.white,
),
),
),
Text(
posts[index].Age,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
],
),
),
);
},
),
This is the most simplest approach but as per your answer there is another complex approach:
ListView.builder(
itemCount: posts.length * 2,
itemBuilder: (context, index) {
if (index.isOdd) {
return Card(
shadowColor: Colors.white,
color: const Color(0xFF303030),
elevation: 1,
child: IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
posts[index*2].Did,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
posts[index*2].Dname,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
posts[index*2].Age,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
),
);
} else {
return Row(
//This row is not getting displayed
children: [
Container(
child: const Text(
'Did',
style: TextStyle(
color: Colors.white,
),
),
),
Container(
child: const Text(
'Dname',
style: TextStyle(
color: Colors.white,
),
),
),
Container(
child: const Text(
'Age',
style: TextStyle(
color: Colors.white,
),
),
),
],
);
}
},
),
It can't be displayed because it isn't used anywhere! Your _buildPosts() only returns Listview.builder().
Wrap your List view.builder() inside a Column, then move your Row() inside that Column() above Listview.builder().
The best and optimal solution for your case is use DataTable .It makes table data visualization so simple and it is very easy to use
#override
Widget build(BuildContext context) {
return DataTable(
columns: const <DataColumn>[
DataColumn(
label: Text(
'Did',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Dname',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
rows: const <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('1')),
DataCell(Text('Raj')),
DataCell(Text('34')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('Janine')),
DataCell(Text('43')),
DataCell(Text('Professor')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('William')),
DataCell(Text('27')),
DataCell(Text('Associate Professor')),
],
),
],
);
}
you can render a list into row like this
rows: _list.map((cita) => DataRow(
cells: [
DataCell(Text(_list.telefono ?? '',style: const
TextStyle(
fontSize: 18,color: Colors.black
))),
DataCell(Text(_list.nombre ?? '',style: const
TextStyle(
fontSize: 18,color: Colors.black
))),
DataCell(Text(_list.nombre ?? '',style: const
TextStyle(
fontSize: 18,color: Colors.black
))),
]
)
I know my answer is far away from your asking question but this is the best approach to handle data table in flutter

How favorite list stays the same when you exit the application in flutter?

I created a database with sqflite on the user's phone. The words entered by the user are saved in the main list.
When you press the icon next to it, it is added to the favorite list, but when the user exits the application, the favorite list is reset.
The main list is the same, there is no problem with it.
When a word is deleted from the main list, it is also deleted from the favorite. But the only problem is that when the favorite list comes out and enters, it resets and writes blank.
How can I make the favorite list stay the same when I exit the application?
This is home page dart file.
class WordList extends StatefulWidget {
const WordList({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _WordListState();
}
}
class _WordListState extends State {
var dbHelper = DbHelper();
List<Word> words;
List<Word> favoriteWords = [];
int wordCount = 0;
int fwordCount = 0;
#override
void initState() {
getWords();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
titleSpacing: 0,
title: Text(
"ShopList",
style: GoogleFonts.caveat(
textStyle: const TextStyle(
color: Colors.white,
fontSize: 35,
letterSpacing: 1,
fontWeight: FontWeight.w700,
),
),
),
leading: const Padding(
padding: EdgeInsets.all(2.0),
child: Icon(
Icons.shopping_cart,
size: 35.0,
),
),
backgroundColor: Colors.deepPurple,
bottom: const TabBar(
indicatorColor: Colors.deepPurpleAccent,
automaticIndicatorColorAdjustment: false,
labelColor: Colors.deepPurple,
unselectedLabelColor: Colors.white,
indicatorSize: TabBarIndicatorSize.label,
indicator: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(7), topRight: Radius.circular(7)),
color: Colors.white),
tabs: [
Tab(
child: Align(
alignment: Alignment.center,
child: Icon(Icons.article_rounded),
),
),
Tab(
child: Align(
alignment: Alignment.center,
child: Icon(Icons.favorite),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
goToWordAdd();
},
child: const Icon(Icons.add),
tooltip: "Add New Item",
splashColor: Colors.white,
backgroundColor: Colors.deepPurple,
),
body: TabBarView(
children: [
ListView.builder(
itemCount: wordCount,
itemBuilder: (BuildContext context, int position) {
return Card(
child: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(3.0),
child: ListTile(
title: SelectableText(
words[position].word,
cursorColor: Colors.purple,
showCursor: false,
toolbarOptions: const ToolbarOptions(
copy: true,
selectAll: true,
cut: false,
paste: false),
style: GoogleFonts.caveat(
textStyle: const TextStyle(
color: Colors.deepPurple,
fontSize: 25.0,
fontWeight: FontWeight.w700,
),
),
),
subtitle: SelectableText(
words[position].description,
cursorColor: Colors.purple,
showCursor: false,
toolbarOptions: const ToolbarOptions(
copy: true,
selectAll: true,
cut: false,
paste: false),
style: GoogleFonts.caveat(
textStyle: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w700,
),
),
),
),
),
),
ElevatedButton(
onPressed: () {
setState(() {
if (!favoriteWords
.contains(words[position])) {
favoriteWords.add(words[position]);
}
});
},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(
Colors.deepPurple,
),
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5.0),
side: const BorderSide(
color:
Colors.deepPurpleAccent)))),
child: const Icon(
Icons.favorite,
color: Colors.white,
),
),
IconButton(
color: Colors.blueGrey,
icon: const Icon(Icons.delete),
tooltip: 'Delete Item',
onPressed: () {
goToDelete(words[position]);
},
),
],
),
);
},
),
favoriteWords.isEmpty
? const Center(
child: Text(
'Ürünlerini favorilerine ekle, almayı unutma! ',
style: TextStyle(
color: Colors.deepPurple,
fontWeight: FontWeight.bold),
),
)
: ListView.builder(
itemCount: fwordCount,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(3.0),
child: ListTile(
title: SelectableText(
favoriteWords[index].word,
cursorColor: Colors.purple,
showCursor: false,
toolbarOptions: const ToolbarOptions(
copy: true,
selectAll: true,
cut: false,
paste: false),
style: const TextStyle(fontSize: 17.0),
),
subtitle: SelectableText(
favoriteWords[index].description,
cursorColor: Colors.purple,
showCursor: false,
toolbarOptions: const ToolbarOptions(
copy: true,
selectAll: true,
cut: false,
paste: false),
style: const TextStyle(fontSize: 17.0),
),
),
),
),
ElevatedButton(
onPressed: () {
setState(() {
favoriteWords.remove(favoriteWords[index]);
});
},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(
Colors.deepPurple,
),
),
child: const Icon(
Icons.remove,
color: Colors.white,
),
),
],
),
);
},
),
],
),
),
);
}
void goToWordAdd() async {
bool result = await Navigator.push(
context, MaterialPageRoute(builder: (context) => const WordAdd()));
if (result != null) {
if (result) {
getWords();
}
}
}
void getWords() {
var wordsFuture = dbHelper.getWords();
wordsFuture.then((data) {
setState(() {
words = data;
wordCount = data.length;
});
});
}
void goToDelete(Word word) async {
await dbHelper.delete(word.id);
favoriteWords.remove(word);
getWords();
}
}

Flutter: RichText not working as expected

I'm new to Flutter and trying to clone an app to learn it. I create an intro look like this in real app: introduction screen in real app
And I use RichText to create that text but somehow it shows the code on the screen: introduction screen in my clone app
Here is the code:
class Body extends StatefulWidget {
const Body({Key? key}) : super(key: key);
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
int currentPage = 0;
final List<Map<String, Object>> _introductionData = [
{
"image": "assets/images/intro_1.png",
"title": "",
"text": RichText(
text: const TextSpan(
style: TextStyle(
fontSize: 12,
color: Colors.white,
),
children: [
TextSpan(
text: 'Sign in first time ',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(text: 'success to get '),
TextSpan(
text: '50% off coupon ',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(text: 'and '),
TextSpan(
text: 'lottery code ',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(text: 'to join '),
TextSpan(
text: '"Download app, get big prize" ',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(text: 'have a chance '),
TextSpan(
text: 'to win a smart tv ',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(text: 'worth nearly 1000\$.')
],
),
),
},
{
"image": "assets/images/intro_2.png",
"title": "Super convenient online pharmacy",
"text": 'Full of great deals, free shipping from 13$. Accumulate Extracare points after every purchase.',
},
{
"image": "assets/images/intro_3.png",
"title": "Consult with a pharmacist online via video.",
"text": 'Advice on prescriptions and drug use from a team of highly qualified pharmacists.'
},
{
"image": "assets/images/intro_4.png",
"title": "Look up drug information and disease symptoms",
"text": 'Update the latest health information, look up information quickly and accurately.',
},
];
#override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: Stack(
fit: StackFit.expand,
children: [
Image.asset(
'assets/images/intro_background.png',
fit: BoxFit.cover,
height: double.infinity,
alignment: Alignment.topCenter,
),
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 16.0),
child: Column(
children: [
Expanded(
flex: 6,
child: PageView.builder(
onPageChanged: (value) {
setState(() {
currentPage = value;
});
},
itemCount: _introductionData.length,
itemBuilder: (context, index) => IntroductionContent(
image: _introductionData[index]['image'].toString(),
title: _introductionData[index]['title'].toString(),
text: _introductionData[index]['text'].toString(),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
_introductionData.length,
(index) => buildDot(index: index),
),
),
const Padding(padding: EdgeInsets.only(top: 25)),
DefaultButton(
text: 'Continue',
backgroundColor: Colors.white,
textColor: kPrimaryColor,
press: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext _context) =>
const AcceptTermsScreen(),
),
);
},
)
],
),
),
)
],
),
),
],
),
);
}
AnimatedContainer buildDot({int? index}) {
return AnimatedContainer(
duration: kAnimationDuration,
margin: const EdgeInsets.only(right: 15),
height: currentPage == index ? 6 : 4,
width: currentPage == index ? 6 : 4,
decoration: BoxDecoration(
color: currentPage == index
? Colors.white
: const Color(0x77FFFFFF),
borderRadius: BorderRadius.circular(3),
),
);
}
}
IntroductionContent code:
class IntroductionContent extends StatelessWidget {
const IntroductionContent({
Key? key,
this.title,
this.text,
this.image,
}) : super(key: key);
final String? title, text, image;
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const Spacer(),
const Spacer(),
Image.asset(
image!,
height: 250,
),
const Spacer(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Text(
title!,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.white,
),
),
),
const Padding(padding: EdgeInsets.only(top: 20)),
Text(
text!,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
height: 1.5,
),
),
],
);
}
}
Thanks in advance for help me fix that.
Change your text type string to RichText and in your pageView, remove the toString from the IntroductionContent text parameter.
PageView.build
PageView.builder(
onPageChanged: (value) {
setState(() {
currentPage = value;
});
},
itemCount: _introductionData.length,
itemBuilder: (context, index) => IntroductionContent(
image: _introductionData[index]['image'].toString(),
title: _introductionData[index]['title'].toString(),
text: _introductionData[index]['text'], // here changed need
),
),
IntroductionContent.class
class IntroductionContent extends StatelessWidget {
const IntroductionContent({
Key key,
this.title,
this.text,
this.image,
}) : super(key: key);
final String title, image;
final RichText text; // change text string to Richtext
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const Spacer(),
const Spacer(),
Image.asset(
image,
height: 250,
),
const Spacer(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.white,
),
),
),
const Padding(padding: EdgeInsets.only(top: 20)),
text, // here just assign your text
],
);
}
}
output:

something problem on alertdialog flutter, you can help me?

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'modalBarrierDismissLabel' was called on null.
This is code
....
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(28.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FlatButton(
child: Text(
"Booking Antrian",
style: TextStyle(color: Colors.white),
),
color: Colors.indigo,
onPressed: () {
createInterstitialAd()
..load()
..show();
},
),
FlatButton(
child: Text(
"Keluar",
style: TextStyle(color: Colors.white),
),
color: Colors.green,
onPressed: () {
createInterstitialAd()
..load()
..show();
},
),
FlatButton(
child: Text(
"About",
style: TextStyle(color: Colors.white),
),
color: Colors.pink,
onPressed: () {
createInterstitialAd()
..load()
..show();
},
),
RaisedButton(
child: Text('Alert with Buttons'),
onPressed: () => _onAlertButtonsPressed(context),
),
],
),
),
);
}
...
_onAlertButtonsPressed(context) {
Alert(
context: context,
//image: Image.network('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg'),
image: Image.asset(
'assets/images/watermark.jpg',
width: 100.0,
height: 100.0,
),
title: "RE",
desc: "Halo.. 😊",
buttons: [
DialogButton(
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.pop(context),
color: Color.fromRGBO(231, 76, 60, 10),
),
DialogButton(
child: Text(
"Share",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.pop(context),
color: Color.fromRGBO(0, 179, 134, 1.0),
),
],
).show();
}
...
in your app delegates add this:
GlobalCupertinoLocalizations.delegate
So in your main MaterialApp Widget:
MaterialApp(
...
localizationsDelegates: [
// A class which loads the translations from JSON files
AppLocalizations.delegate,
// Built-in localization of basic text for Material widgets
GlobalMaterialLocalizations.delegate,
// Built-in localization for text direction LTR/RTL
GlobalWidgetsLocalizations.delegate,
// Built-in localization of basic text for Cupertino widgets
GlobalCupertinoLocalizations.delegate
],
)

Categories

Resources