How to make a favorites button clickable in gridview builder - android

I have a favorite button in a gridview.builder, but i want when i click a particular favorite button (it turns to red) and others wont turn... cos when i click a particular favorite button it affects all. I need help on how to solve it.
i am new to flutter.
Please i will like someone to mentor me...i seriously need a mentor(Please anyone)
class HomeItemsWidget extends StatefulWidget {
const HomeItemsWidget({
Key? key,
}) : super(key: key);
#override
State<HomeItemsWidget> createState() => _HomeItemsWidgetState();
}
class _HomeItemsWidgetState extends State<HomeItemsWidget> {
bool isIconView = false;
`your text`
#override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: gridImages.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
mainAxisExtent: 231.5,
crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: BoxDecoration(`your text`
color: Appcolors.whiteColor,
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.only(left: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(
onPressed: () {
toggleIconView();
},
icon: Icon(isIconView ? Icons.favorite : Icons.favorite_border),
color: isIconView ? Colors.red : Colors.red ,
),
Align(
alignment: Alignment.center,
child: Image.asset(
"${gridImages[index]["image"]}",
height: 70.0,
),
),
const SizedBox(height: 20.0),
Text(
"${gridImages[index]["heading"]}",
style: GoogleFonts.poppins(
color: Appcolors.primaryColor,
fontSize: 12.0,
fontWeight: FontWeight.w500),
),
const SizedBox(height: 4.0),
Text(
"${gridImages[index]["title"]}",
style: GoogleFonts.poppins(
color: Appcolors.greyColor,
fontSize: 16.0,
fontWeight: FontWeight.w600),
),
const SizedBox(height: 12.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${gridImages[index]["price"]}",
style: GoogleFonts.poppins(
color: Appcolors.darkGreyColor,
fontSize: 14.0,
fontWeight: FontWeight.w500),
),
],
),
),
);
},
);
}
void toggleIconView() {
setState(() {
isIconView = !isIconView;
});
}
}
i want when i click a particular favorite button (it turns to red) and others wont turn

You need to use List to track N-number of items. You can do
class _HomeItemsWidgetState extends State<HomeItemsWidget> {
List<int> selectedItem = [];
And to get and set color based on current state
IconButton(
onPressed: () {
toggleIconView(index);
},
icon: Icon(selectedItem.contains(index)
? Icons.favorite
: Icons.favorite_border),
color: selectedItem.contains(index) ? Colors.red : Colors.red,
),
And toggle option will be
void toggleIconView(int index) {
if (selectedItem.contains(index)) {
selectedItem.remove(index);
} else {
selectedItem.add(index);
}
setState(() {});
}

Related

Updating State of StatefulWidget from other StatefulWidget in Flutter?

i am trying to update the state of the animated switcher in the middle area. i am trying to do this using a setstate in the lower area. but it does not work.
the first thing i did is to create a variable with a boolean data type in the home class.
then i passed the variable to both the middle area and the lower area
the idea was that if the same variable is passed to the class whose state i am trying to update, and the class with the set state, it would work. but it seems i am wrong. i would appreciate some assistance.
the boolean variable i am trying to make work is the _rep variable
This is the Home widget
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
late AnimationController _animationController;
late AnimationController _controller;
late Animation<Offset> _animation;
late Animation<Offset> _anime;
bool _rep = true;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2)
);
_animation = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,3.0),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
_anime = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,-0.55),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
super.initState();
}
#override
void dispose() {
_animationController.dispose();
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(top: 3.h),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TopIcon(icons: Icons.arrow_back, color:Colors.grey.shade300 ,),
SizedBox(
height: 13.h,
width: 13.w,
child: Image.asset('assets/images/download.png')
),
TopIcon(icons: Icons.shopping_bag_outlined, color: Colors.grey.shade300,),
],
),
SizedBox(
height: 3.h,
),
Text('Frappuccino',
style: TextStyle(
fontSize: 27.sp,
fontWeight: FontWeight.bold
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('White Chocolate',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey.shade400
),
),
),
MiddleArea(
controller: _animationController,
animation: _animation,
rep: _rep,
),
LowerArea(controller: _animationController, anime: _anime, rep = _rep),
],
),
),
),
);
}
}
This is the middle area
class MiddleArea extends StatefulWidget {
MiddleArea({Key? key, required this.controller, required this.animation, required this.rep}) : super(key: key);
AnimationController controller;
Animation<Offset> animation;
final bool rep;
#override
State<MiddleArea> createState() => _MiddleAreaState();
}
class _MiddleAreaState extends State<MiddleArea> {
bool _flag = true;
bool _favourite = true;
#override
Widget build(BuildContext context) {
print(widget.rep);
return SizedBox(
height: 52.h,
child: Stack(
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 135.0),
child: Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade200,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade100,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade50,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
],
),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
SizeAndFave(text: 'Preference'),
SizeAndFave(text: 'Fave!')
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: (){
setState(() {
_flag = !_flag;
});
},
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: widget.rep == true?Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
)
),
GestureDetector(
onTap: (){
setState(() {
_favourite = !_favourite;
});
},
child: _favourite? TopIcon(icons: Icons.favorite_border, color: Colors.brown.shade300)
:TopIcon(
icons: Icons.favorite, color: Colors.brown.shade300)
)
],
)
],
),
AnimatedSwitcher(
duration: Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition( opacity: animation,
child: child);
},
child: _flag == true ? Center(
key: const Key('1'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/starcup.png'),
),
),
):Center(
key: const Key('2'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/greeen.png'),
),
),
),
),
Positioned(
child:
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(top: 30.h),
child: TopIcon(
icons: Icons.car_crash_outlined, color: Colors.brown.shade300),
),
)),
const Positioned(
child:
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: EdgeInsets.only(top: 330.0, left: 14),
child: Text('\$ 5.99',
style: TextStyle(
fontSize: 27,
fontWeight: FontWeight.bold
),
),
),
))
],
),
);
}
}
and lastly, the lower area
class LowerArea extends StatefulWidget {
final AnimationController controller;
final Animation<Offset> anime;
bool rep;
LowerArea({Key? key, required this.controller, required this.anime, required this.rep}) : super(key: key);
#override
State<LowerArea> createState() => _LowerAreaState();
}
class _LowerAreaState extends State<LowerArea> {
bool _bigger = true;
bool _fade = true;
void move(){
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.all(1.h),
child: const Text('Tall Frappuccino',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.only(right: 5.h),
child: const Text('Swipe Down',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.all(2.h),
child: const Text('Pickup',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
)
],
),
),
SlideTransition(
position: widget.anime,
child: AnimatedContainer(
// height: 11.h,
width: _bigger ? 35.h: 80.h,
duration: const Duration(milliseconds: 500),
child: Stack(
fit: StackFit.loose,
children: [
Center(child: Image.asset('assets/images/baggie.png')),
Center(
child: Padding(
padding: EdgeInsets.only(bottom: 4.h),
child: GestureDetector(
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
widget.rep = !widget.rep;
print('this is fade $_fade ');
});
},
child: AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: _fade? Container(
key: Key('1'),
height: 8.h,
width: 7.w,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(15)
),
child: Column(
children: [
Padding(
padding: EdgeInsets.all(0.3.h),
child: Icon(
Icons.lock_outline,
color: Colors.white54,
size: 2.5.h,
),
),
Icon(
Icons.arrow_drop_down,
color: Colors.white12,
size: 3.h,
),
],
),
):null,
),
),
),
)
],
),
),
)
],
);
}
use provider pacakges https://pub.dev/packages/provider
Create a class that inherits the functions of ChangeNotifyer to create a flag to control and create a setter.
provider class
class StateController extends ChangeNotifier{
bool _req = false;
bool get req => _req; //getter
setReqValue(){
_req = !_req;
notifyListener();
}
}
Declare the provider class in the main function. You can change the location of the declaration according to Wiget tree, but first declare it in main
Change main.dart
void main(){
runApp(
Multiprovider(
providers: [
ChangeNotifierProvider(create: (_) => StateController()),
],
child: HomePage(),
)
);
}
The UI is updated by notifyListener().
change UI
child: context.watch<StateController>().req == true ? Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
Change State
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
context.read<StateController>().setReqValue();
print('this is fade $_fade ');
});
},
I am too lazy to try understand your code. But if you want to update state of the page after you pop from Navigation to it.
In page you want to update
Navigation.push(context, /* Page that will change something */)
// Future will trigger then you return to this page.
.then((_) => setState(() {}))

Method not found: 'miniPlayer'

I have a page with name playerPage which contains the bottomNavigationBar which has got mini player and navigation items respectively. I also have a MyHomePage which contains the songs to be played. I created a constructor with Function name miniPlayer in MyHomePage to call _miniPlayer method in the playerPage but I am getting an error that says Method not found: 'miniPlayer'.
MyHomePage
class MyHomePage extends StatefulWidget {
final Function miniPlayer;
const MyHomePage(this.miniPlayer);
// const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState(miniPlayer);
}
class _MyHomePageState extends State<MyHomePage> {
final Function miniPlayer;
_MyHomePageState(this.miniPlayer);
Widget _recommended(context) {
return Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("Worship").snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if(snapshot.hasData) {
final snap = snapshot.data!.docs;
return ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: snap.length,
itemBuilder: (context, index) {
return Stack(
children: [
GestureDetector(
child:Container(
height:50,
width: MediaQuery.of(context).size.width,
child: Card(
child: Center(
child: Row (
children : [
Text(
snap[index]['name'],
textAlign: TextAlign.start,
style: const TextStyle(
color: Colors.black54,
fontWeight: FontWeight.bold,
),
),
SizedBox(width:50),
Text(
snap[index]['title'],
textAlign: TextAlign.start,
style: const TextStyle(
color: Colors.black54,
fontWeight: FontWeight.bold,
),
),
]
)),
)),
onTap: () {
var ur= snap[index]['url'];
var ti =snap[index]['title'];
var music =snap[index]['music'];
**miniPlayer(url:ur,le:ti,music:music);** Getting error here
},
)
],
// ),
);
},
);
} else {
return const SizedBox();
}
},
)
],
);
}
PlayerPage
this is the miniPlayer method in the playerPage
Widget miniPlayer(le,url,music) {
this.le =le;
this.url =url;
this.music =music;
setState(() {
});
if(widget.le.isEmpty) {
return SizedBox();
}
return AnimatedContainer(
duration: const Duration(milliseconds: 500),
color: Colors.indigo,
height: MediaQuery.of(context).size.height * 0.07,
width: MediaQuery.of(context).size.width,
child: Row(
children: [
Container(
padding: EdgeInsets.only(left: 6),
child: CircleAvatar(
backgroundColor: Colors.indigo,
radius: 20,
child: IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow_rounded,
color: Colors.white,
),
onPressed: () async {
if (isPlaying) {
await audioPlayer.pause();
} else {
await audioPlayer.resume();
}
},
),
),
),
const SizedBox(
width: 10,
),
Container(
padding: EdgeInsets.only(left: 5),
width: MediaQuery.of(context).size.width * 0.85,
child: Column(
children: [
Expanded(
child: Marquee(
textDirection: TextDirection.ltr,
velocity: 30,
blankSpace: 90,
//pauseAfterRound: const Duration(seconds: 2),
text: widget.le,
style: const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
))
],
))
],
),
);
}
First of all, here it's not properly understood. It's a little bit messy. But one thing is make sure you pass it down the widget tree. And another thing is try not to pass properties through private constructors.
You can easily access that property (aka. miniPlayer function) just by typing widget.miniPlayer. In that way you can be more sure that this function is available in the main widget class.
Also you're returning an widget inside a function. Make sure you use it. Other wise that widget getting returned won't do anything inside a function.

Flutter - How to change the visibility of a single widget in a widget list?

I pull some variables in Firebase and I create a widget list with these variables. I want to control widget visibility when I click a widget. When I use the Visibility widget and set "visible: widgetVisibility" value, all widgets are changed at the same time. I only want the widget I clicked to change. How can I do that?
body: StreamBuilder<QuerySnapshot>(
stream: _keyService.getKeys(),
builder: (context, snapshot) {
return !snapshot.hasData
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot mypost = snapshot.data!.docs[index];
return Padding(
padding: EdgeInsets.all(size * 0.3),
child: InkWell(
onTap: () {
valueVisible = !valueVisible;
},
child: Container(
decoration: BoxDecoration(
color: ColorItems.mainColor,
border: Border.all(width: 5, color: Colors.grey),
borderRadius: BorderRadius.all(Radius.circular(20))),
child: Padding(
padding: EdgeInsets.all(size),
child: Container(
child: Row(
children: [
Expanded(
child: Text(
"${mypost['key']}",
style: const TextStyle(
color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
),
const Text(
": ",
style:
TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(
width: 20,
),
Expanded(
child: Visibility(
visible: valueVisible,
child: Text(
"${mypost['value']}",
style: const TextStyle(
color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
))
],
),
),
),
),
),
);
},
);
})
Additionally, screenshots is here..
This might not be the optimal solution, but I always create a List for that purpose:
instead of one valueVisible bool I create a List and add a bool for each item in the list.
...itemBuilder: (context, index) { valueVisibleList.add(true)...
and in the button I then use the current item index to change only the corresponding bool
onTap: () { setState(({
valueVisibleList[index] = !valueVisibleList[index];
})
},
Just use a Map<String, bool> where the keys are the post's key and the value its visibility. The visibility should default to true if the key is not present. And the state should only change inside a setState function.
It would be something like the following (Check out the live demo on DartPad):
import 'package:flutter/material.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: 'Flutter Demo',
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
final posts = [
for (var i = 0; i < 100; i++) {'key': 'key$i', 'value': '$i'}
];
class _MyHomePageState extends State<MyHomePage> {
final valueVisible = <String, bool>{};
#override
Widget build(BuildContext context) {
const size = 16.0;
return Scaffold(
body: ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final mypost = posts[index];
return Padding(
padding: const EdgeInsets.all(size * 0.3),
child: InkWell(
onTap: () {
setState(() {
valueVisible[mypost['key']!] =
!(valueVisible[mypost['key']!] ?? true);
});
},
child: Container(
decoration: BoxDecoration(
color: const Color(0xffff9400),
border: Border.all(width: 5, color: Colors.grey),
borderRadius: const BorderRadius.all(Radius.circular(20))),
child: Padding(
padding: const EdgeInsets.all(size),
child: Row(
children: [
Expanded(
child: Text(
"${mypost['key']}",
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
),
const Text(
": ",
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
const SizedBox(
width: 20,
),
Expanded(
child: Visibility(
visible: valueVisible[mypost['key']!] ?? true,
child: Text(
"${mypost['value']}",
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
),
)
],
),
),
),
),
);
},
),
);
}
}

Why my list is not updating while using setState in Flutter

I made a list of class OriginDestination, i.e. _allCities in my file. I then assigned all values in filteredCities and cities in initState.
Then I made a function runFilter which would take keyword from TextField and filter the results accordingly and save them to resultCities. Then I am using resultCities to display the information in ListView.builder. But the problem is, the list is not filtering according to the keyword i am searching.
Also, it would be appreciated if you can suggest a better way of using parameter cities, i.e. I don't think that passing the cities as parameter through state's constructor is a good practice.
Here is the code -
import 'package:flutter/material.dart';
import 'package:passenger_flutter_app/models/new_city.dart';
import 'package:passenger_flutter_app/models/origin_destination.dart';
import 'package:passenger_flutter_app/utils/colors.dart';
class SelectionScreen extends StatefulWidget {
List<OriginDestination>? cities;
SelectionScreen({this.cities});
#override
_SelectionScreenState createState() => _SelectionScreenState(cities);
}
class _SelectionScreenState extends State<SelectionScreen> {
final List<OriginDestination>? _allCities;
_SelectionScreenState(this._allCities);
bool originSelected=false;
List<OriginDestination>? resultCities = [];
List<OriginDestination>? filteredCities = [];
void getCitiesFromResponse() {
/*for(var city in _allCities!) {
cities!.add(city.origin!);
}*/
filteredCities=_allCities;
resultCities=_allCities;
}
#override
initState() {
// at the beginning, all users are shown
getCitiesFromResponse();
super.initState();
}
void _runFilter(String enteredKeyword) {
if (enteredKeyword.isEmpty) {
// if the search field is empty or only contains white-space, we'll display all users
filteredCities = _allCities;
} else {
filteredCities = _allCities!
.where((city) =>
city.origin!.name!.toLowerCase().contains(enteredKeyword.toLowerCase()))
.toList();
// we use the toLowerCase() method to make it case-insensitive
}
#override
void setState() {
resultCities=filteredCities;
}
}
#override
Widget build(BuildContext context) {
var originSelected;
return SafeArea(
child: Scaffold(
backgroundColor: const Color(0xffEEEDEF),
body: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width*0.8,
),
Row(
children: [
IconButton(
icon: const Icon(Icons.arrow_back),
color: Colors.orange,
onPressed: () {
Navigator.pop(context);
},
),
Column(
children: [
originSelected==true ? Container(
child: Text(''),
) :
Container(
width: MediaQuery.of(context).size.width * 0.85,
height: 50.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
const BorderRadius.all(Radius.circular(5.0)),
border: Border.all(color: colorAccent)),
child: TextField(
decoration: InputDecoration(
hintText: "Enter Origin",
border: InputBorder.none,
contentPadding: const EdgeInsets.only(left: 10.0),
hintStyle: TextStyle(
fontSize: 15.0,
color: Colors.grey[500],
),
),
onChanged: (value) {
_runFilter(value);
},
),
),
],
),
],
),
],
),
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.width * 0.04, top: 3.0),
child: Text(
'Popular Searches:',
style: TextStyle(
color: popUpLightTextColor,
fontSize: MediaQuery.of(context).size.width * 0.035),
),
),
),
Expanded(
child: ListView.builder(
itemCount: resultCities!.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(
left: 18.0, top: index==0 ? 29.0 : 15.0, bottom: 15.0),
child: InkWell(
onTap: () {
print(resultCities?[index].origin!.name);
/*setState(() {
widget.city=filteredCities[index]['city'];
print("Changed to - ");
//print(widget.city);
Navigator.pop(context);
});*/
},
child: Text(
resultCities?[index].origin!.name??"No name",
style: const TextStyle(
color: darkText,
fontSize: 15.0,
fontWeight: FontWeight.normal,
),
),
),
);
},
),
),
],
),
),
);
}
}
seems like you defined the setState function instead of calling it, so instead of:
#override
void setState() {
resultCities=filteredCities;
}
write:
setState(() {
resultCities=filteredCities;
});
Why are you overriding the setState. You also pass the call back as argument in the setState.
You should be call setState on a trigger, like a gesture or button:
setState(() => resultCities = filteredCities);

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:

Categories

Resources