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.
Related
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(() {});
}
I am learning Flutter now and i have a screen that loading many series characters, and i have an icon in the AppBar showing
ModalBottomSheet and it have many choices to filter to update data with the new chosen ones.
I got the choices and stored the in a List of Strings but i dont know how to pass it to a function in my cubit file and i don't know how to update data using cubit , i tried many ways but didn't work with me.
Here's my code
import 'package:breakingbad/data/models/breaking_bad_characters.dart';
import 'package:breakingbad/domain/cubit/rick_and_morty_cubit.dart';
import 'package:breakingbad/presentation/character-detail.dart';
import 'package:conditional_builder_null_safety/conditional_builder_null_safety.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../widgets/character_item.dart';
import '../widgets/chip_choice.dart';
class MyHomePage extends StatefulWidget {
static String id ='home';
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return BlocConsumer<RickAndMortyCubit,RickAndMortyState>(
listener: (context, state){},
builder: (context, state) {
var cubit = RickAndMortyCubit.get(context);
return Scaffold(
appBar: AppBar(
title: const Center(child: Text('Rick And Morty')),
actions: [
IconButton(
padding: const EdgeInsets.only(right: 25),
onPressed: (){
showModalBottomSheet(
context: context,
builder: (context) =>
Column(
children: [
Container(
alignment: Alignment.center,
color: Colors.black,
width: double.infinity,
height: 50,
child: const Text('Filter Characters',style: TextStyle(color:
Colors.white,fontSize: 22),),
),
const ChoiceChipWidget(),
],
)
);
},
icon: const Icon(Icons.filter_list_sharp,size: 40,)),
],
),
body: ConditionalBuilder(
condition: state is RickAndMortyCharsLoaded,
fallback: (context)=> const Center(child: CircularProgressIndicator()),
builder: (context) => GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 2 / 3,
crossAxisSpacing: 1,
mainAxisSpacing: 1,
),
itemCount: cubit.allHuman.length,
itemBuilder: (context,index) =>
CharacterItem(
rickAndMortyCharacters: cubit.allHuman[index],
widget:CharacterDetail(rickAndMortyCharacters: cubit.allHuman[index]),
)
),
)
);
}
);
}
}
import 'package:breakingbad/domain/cubit/rick_and_morty_cubit.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ChoiceChipWidget extends StatefulWidget {
const ChoiceChipWidget( {super.key, });
#override
State<ChoiceChipWidget> createState() => _ChoiceChipWidgetState();
}
class _ChoiceChipWidgetState extends State<ChoiceChipWidget> {
String selectedChoice1 = "";
String selectedChoice2 = "";
String selectedChoice3 = "";
List<String> filter=[];
#override
Widget build(BuildContext context) {
return BlocBuilder<RickAndMortyCubit, RickAndMortyState>(
builder: (context, state) {
var cubit = RickAndMortyCubit.get(context);
return Column(
children: [
Chip(
elevation: 20,
padding: const EdgeInsets.all(8),
backgroundColor: Colors.greenAccent[100],
shadowColor: Colors.black,
label: const Text(
'Gender',
style: TextStyle(fontSize: 20),
), //Text
),
Row(
children: cubit.gender.map((item){
return Container(
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
label: Text(item),
labelStyle: const TextStyle(
color: Colors.black, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22.0),
),
backgroundColor: Colors.grey[300],
selectedColor: Colors.blue,
selected: selectedChoice1 == item,
onSelected: (selected) {
setState(() {
selectedChoice1 = item;
filter.add(selectedChoice1);
});
},
),
);
}).toList()
),
Chip(
elevation: 20,
padding: const EdgeInsets.all(8),
backgroundColor: Colors.greenAccent[100],
shadowColor: Colors.black,
label: const Text(
'Species',
style: TextStyle(fontSize: 20),
), //Text
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children:cubit.species.map((item){
return Container(
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
label: Text(item),
labelStyle: const TextStyle(
fontSize: 18,
color: Colors.black, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22.0),
),
backgroundColor: Colors.grey[300],
selectedColor: Colors.blue,
selected: selectedChoice2 == item,
onSelected: (selected) {
setState(() {
selectedChoice2 = item;
filter.add(selectedChoice2);
});
},
),
);
}).toList()
),
),
Chip(
elevation: 20,
padding: const EdgeInsets.all(8),
backgroundColor: Colors.greenAccent[100],
shadowColor: Colors.black,
label: const Text(
'Statue',
style: TextStyle(fontSize: 20),
), //Text
),
Row(
children:cubit.statue.map((item){
return Container(
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
label: Text(item),
labelStyle: const TextStyle(
color: Colors.black, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22.0),
),
backgroundColor: Colors.grey[300],
selectedColor: Colors.blue,
selected: selectedChoice3 == item,
onSelected: (selected) {
setState(() {
selectedChoice3 = item;
filter.add(selectedChoice3);
});
},
),
);
}).toList()
),
SizedBox(height: 4,),
InkWell(
onTap: (){
// here i want to apply changes
Navigator.pop(context);
},
child: Chip(
elevation: 20,
padding: const EdgeInsets.all(8),
backgroundColor: Colors.greenAccent[100],
shadowColor: Colors.black,
label: const Text(
'apply',
style: TextStyle(fontSize: 20),
), //Text
),
),
],
);
},
);
}
}
I have two main functions, the first one get all characters to the home screen:
List<Characters>?
getAllCharacters(){
myRepository.
getAllCharacters().then(
(allCharacters)=>
this.allCharacters =
allCharacters;
);
return allCharacters;
}
and the second function that takes a parameter of List of Strings which i suppose to get them from the filtering in bottomSheet;
List<Characters>?
getFilteredCharacters
(List<String> list){
myRepository
.getFilteredCharacters(list)
.then((filteredCharacters)=>
this.filteredCharacters =
filteredCharacters;
);
return filteredCharacters;
}
Anyone could help me in this issue?
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(() {}))
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),
),
),
)
],
),
),
),
),
);
},
),
);
}
}
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);