Flutter callbacks - Removing a widget in a list from child - flutter

I have this app where you type in some text and press a button which adds this text to a custom widget. Here is the code:
import 'dart:core';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int count = 0;
TextEditingController noteSend = TextEditingController();
List<String> noteString = [];
#override
Widget build(BuildContext context) {
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(i,
noteRec: noteString[i], noteString: noteString, count: count));
return new Scaffold(
appBar: new AppBar(title: new Text('some title')),
body: Column(
children: <Widget>[
Container(
child: TextField(
controller: noteSend,
),
color: Colors.lightBlueAccent,
width: 150,
height: 50,
),
Expanded(
child: ListView(
children: children,
scrollDirection: Axis.vertical,
),
),
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
setState(() {
noteString.insert(
noteString.length,
noteSend.text,
);
count = count + 1;
});
},
));
}
}
class InputWidget extends StatefulWidget {
final int index;
final String noteRec;
final List<String> noteString;
final int count;
InputWidget(this.index, {Key key, this.noteRec, this.noteString, this.count})
: super(key: key);
#override
_InputWidgetState createState() => _InputWidgetState();
}
class _InputWidgetState extends State<InputWidget> {
#override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () {
// <-- onLongPress
setState(() {
widget.noteString.removeAt(widget.index);
});
},
child: new Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Icon(
Icons.image,
size: 75,
)
],
),
Container(
margin: EdgeInsets.only(left: 80, right: 30),
child: Column(
children: <Widget>[
Text('Note'),
],
),
),
Column(
children: <Widget>[
Text("${widget.noteRec}"),
],
),
],
),
),
);
}
}
I've tried to wrap the custom which in a gesture detector and then add a onLongPress which removes the widget at the index but it's not working.
How can I remove the widget I long press on ?
Thank you

For this scenario, you'll need to use a callback. Because of the scope of the variables, you can't directly delete a variable from noteString in the InputWidget() due to scope, however, it HAS to be triggered by InputWidget because the index information is contained in that widget and has to be used to remove the items from the noteString List, as well as removing the InputWidget from the children List. Therefore, it's callback time.
Callbacks work like this:
1. define a variable that will receive a function in the child.
final Function(int) onDelete;
2. Call the function in the child and pass in the needed variable:
onLongPress: () {
widget.onDelete(widget.index);
},
3. Define the function in the parent THAT YOU WANT TO USE IN THE PARENT and then pass it to the child:
Function(int) onDeleteVar = (int val) {
setState(
() => {
noteString.removeAt(val),
count--,
children.removeAt(val),
},
);
};
children = List.generate(
count,
(int i) => new InputWidget(i,
noteRec: noteString[i],
noteString: noteString,
count: count,
onDelete: onDeleteVar));
Here's a dartpad to see it in action:
http://dartpad.dev/a25e9c402a90fefc778bcfac27aee242
And here's the code:
import 'dart:core';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
MyHomePageState createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
int count = 0;
TextEditingController noteSend = TextEditingController();
List<String> noteString = [];
#override
Widget build(BuildContext context) {
List<Widget> children;
Function(int) onDeleteVar = (int val) {
setState(
() => {
print("noteStringBefore: $noteString"),
print('childrenBefore: $children'),
print(val),
noteString.removeAt(val),
count--,
children.removeAt(val),
print("noteStringAfter: $noteString"),
print('childrenAfter $children'),
},
);
};
children = List.generate(
count,
(int i) => new InputWidget(i,
noteRec: noteString[i],
noteString: noteString,
count: count,
onDelete: onDeleteVar));
return new Scaffold(
appBar: new AppBar(title: new Text('some title')),
body: Column(
children: <Widget>[
Container(
child: TextField(
controller: noteSend,
),
color: Colors.lightBlueAccent,
width: 150,
height: 50,
),
Expanded(
child: ListView(
children: children,
scrollDirection: Axis.vertical,
),
),
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
setState(() {
noteString.insert(
noteString.length,
noteSend.text,
);
count = count + 1;
});
},
));
}
}
class InputWidget extends StatefulWidget {
final int index;
final String noteRec;
final List<String> noteString;
final int count;
final Function(int) onDelete;
InputWidget(this.index,
{Key key, this.noteRec, this.noteString, this.count, this.onDelete})
: super(key: key);
#override
_InputWidgetState createState() => _InputWidgetState();
}
class _InputWidgetState extends State<InputWidget> {
#override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () {
// <-- onLongPress
widget.onDelete(widget.index);
},
child: new Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Icon(
Icons.image,
size: 75,
)
],
),
Container(
margin: EdgeInsets.only(left: 80, right: 30),
child: Column(
children: <Widget>[
Text('Note'),
],
),
),
Column(
children: <Widget>[
Text("${widget.noteRec}"),
],
),
],
),
),
);
}
}

Related

How can I scroll down and focus to a specific widget in flutter?

I am implementing a tutorial of app using https://pub.dev/packages/tutorial_coach_mark . This marked button of beyond the view. So when I need to target this button, I need to scroll/focus this specific part. But I can not find any solution. Can anyone help me with that please?
One Idea is to , Make one Listview with all your widgets . then
Use this :
scroll_to_index: ^2.1.1
Example:
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Scroll To Index Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Scroll To Index Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const maxCount = 100;
static const double maxHeight = 1000;
final random = math.Random();
final scrollDirection = Axis.vertical;
late AutoScrollController controller;
late List<List<int>> randomList;
#override
void initState() {
super.initState();
controller = AutoScrollController(
viewportBoundaryGetter: () =>
Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
axis: scrollDirection);
randomList = List.generate(maxCount,
(index) => <int>[index, (maxHeight * random.nextDouble()).toInt()]);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [
IconButton(
onPressed: () {
setState(() => counter = 0);
_scrollToCounter();
},
icon: Text('First'),
),
IconButton(
onPressed: () {
setState(() => counter = maxCount - 1);
_scrollToCounter();
},
icon: Text('Last'),
)
],
),
body: ListView(
scrollDirection: scrollDirection,
controller: controller,
children: randomList.map<Widget>((data) {
return Padding(
padding: EdgeInsets.all(8),
child: _getRow(data[0], math.max(data[1].toDouble(), 50.0)),
);
}).toList(),
),
floatingActionButton: FloatingActionButton(
onPressed: _nextCounter,
tooltip: 'Increment',
child: Text(counter.toString()),
),
);
}
int counter = -1;
Future _nextCounter() {
setState(() => counter = (counter + 1) % maxCount);
return _scrollToCounter();
}
Future _scrollToCounter() async {
await controller.scrollToIndex(counter,
preferPosition: AutoScrollPosition.begin);
controller.highlight(counter);
}
Widget _getRow(int index, double height) {
return _wrapScrollTag(
index: index,
child: Container(
padding: EdgeInsets.all(8),
alignment: Alignment.topCenter,
height: height,
decoration: BoxDecoration(
border: Border.all(color: Colors.lightBlue, width: 4),
borderRadius: BorderRadius.circular(12)),
child: Text('index: $index, height: $height'),
));
}
Widget _wrapScrollTag({required int index, required Widget child}) =>
AutoScrollTag(
key: ValueKey(index),
controller: controller,
index: index,
child: child,
highlightColor: Colors.black.withOpacity(0.1),
);
}
This will work Perfectly
final dataKey = new GlobalKey();
SingleChildScrollView(
child: Column(
children: [
otherwidgets(),
otherwidgets(),
Container(
key: controller.dataKey,
child: helpPart(context),
),
otherwidgets(),
otherwidgets(),
],
),
on action: Scrollable.ensureVisible(dataKey.currentContext!);
This worked for me!

Get the rack to update after shuffle

In the example below, what is the best construct to use to get the rack to update after a shuffle?
It seems to me that when a StatefulWidget is created, with its corresponding State Object (SO), any method that you can call from elsewhere is a method that's attached to the widget itself (not to the SO).
But, to get the widget to update its display, the SetState() method can only go in the SO's method(s). So how does the method on the widget call a method on its SO?
import 'package:flutter/material.dart';
List<Block> g_blocks = [Block(Colors.red), Block(Colors.green), Block(Colors.blue)];
Rack g_rack = new Rack();
void main() => runApp(MyApp());
// This widget is the root of your application.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
fontFamily: 'PressStart',
),
home: MyHomeScreen(),
);
}
}
class MyHomeScreen extends StatefulWidget {
MyHomeScreen({Key key}) : super(key: key);
createState() => MyHomeScreenState();
}
class MyHomeScreenState extends State<MyHomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: Text('Thanks for your help')),
backgroundColor: Colors.pink,
),
body: Center(
child: g_rack,
),
bottomNavigationBar: SizedBox(
height: 100.0,
child: BottomNavigationBar(
currentIndex: 0,
iconSize: 48.0,
backgroundColor: Colors.lightBlue[100],
items: [
BottomNavigationBarItem(
label: 'Shuffle',
icon: Icon(Icons.home),
),
BottomNavigationBarItem(
label: 'Shuffle',
icon: Icon(Icons.home),
),
],
onTap: (int indexOfItem) {
setState(() {
g_blocks.shuffle;
rack.updateScreen(); // ** How to get the rack to update? **
});
},
),
),
);
} // build
} // End class MyHomeScreenState
class Rack extends StatefulWidget {
#override
_rackState createState() => _rackState();
}
class _rackState extends State<Rack> {
#override
Widget build(BuildContext context) {
return Container(
height: 150.0,
color: Colors.yellow[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: g_blocks),
);
}
void updateRack(){
setState(() {
g_blocks.shuffle;
});
}
}
class Block extends StatelessWidget {
final Color color;
Block(this.color);
#override
Widget build(BuildContext context) {
return Container(height:50,width:50, color: color,);
}
}
Here is a solution where I try to decouple the State Management and Business Logic of the application from the User Interface.
I used the following packages:
freezed for the Domain Entities
hooks_riverpod for the State Management
1. Domain Layer: Entities
We need two Entities to model our Racks of Blocks.
Blocks are defined by their color.
Blocks have no business logic.
Racks are ordered lists of Blocks.
Racks can get shuffled.
Racks can be randomly created for a (random or given) number of Blocks
#freezed
abstract class Block with _$Block {
const factory Block({Color color}) = _Block;
}
#freezed
abstract class Rack implements _$Rack {
const factory Rack({List<Block> blocks}) = _Rack;
const Rack._();
static Rack create([int nbBlocks]) => Rack(
blocks: List.generate(
nbBlocks ?? 4 + random.nextInt(6),
(index) => Block(
color: Color(0x66000000 + random.nextInt(0xffffff)),
),
),
);
Rack get shuffled => Rack(blocks: blocks..shuffle());
}
We use the freeze package to have immutability and the precious copyWith method to manage our States.
2. Application Layer: State Management
We use Hooks Riverpod for our State Management. We just need one StateNotifier and its provider.
This StateNotifierProvider gives access to both the Rack State and the core functionalities that are deal() and shuffle().
class RackStateNotifier extends StateNotifier<Rack> {
static final provider =
StateNotifierProvider<RackStateNotifier>((ref) => RackStateNotifier());
RackStateNotifier([Rack state]) : super(state ?? Rack.create());
void shuffle() {
state = state.shuffled;
}
void deal() {
state = Rack.create();
}
}
3. Presentation Layer: User Interface
The User Interface is made of four Widgets:
AppWidget [StatelessWidget]
HomePage [HookWidget]
RackWidget [StatelessWidget]
BlockWidget [StatelessWidget]
As you see, the only Widget that really cares about the State of the Application is the HomePage.
3.1 AppWidget
class AppWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.amber,
accentColor: Colors.black87,
),
home: HomePage(),
);
}
}
3.2 HomePage
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final rack = useProvider(RackStateNotifier.provider.state);
return Scaffold(
appBar: AppBar(
title: Row(
children: const [
Icon(Icons.casino_outlined),
SizedBox(
width: 8.0,
),
Text('Rack Shuffler'),
],
),
),
body: Center(
child: RackWidget(rack: rack),
),
bottomNavigationBar: BottomAppBar(
color: Theme.of(context).primaryColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
icon: Icon(Icons.refresh),
iconSize: 48,
onPressed: () => context.read(RackStateNotifier.provider).deal(),
),
IconButton(
icon: Icon(Icons.shuffle),
iconSize: 48,
onPressed: () =>
context.read(RackStateNotifier.provider).shuffle(),
),
],
),
),
);
}
}
rack is provided by our StateNotifierProvider, in watch mode:
final rack = useProvider(RackStateNotifier.provider.state);
The Racks are dealt and shuffled using the same provider, in read mode:
...
context.read(RackStateNotifier.provider).deal(),
...
context.read(RackStateNotifier.provider).shuffle(),
...
3.3 RackWidget
class RackWidget extends StatelessWidget {
final Rack rack;
const RackWidget({Key key, this.rack}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: LayoutBuilder(
builder: (context, constraints) {
return Row(
children: rack.blocks
.map((block) => BlockWidget(
block: block,
size: constraints.biggest.width / rack.blocks.length))
.toList(),
);
},
),
);
}
}
Basic StatelessWidget. We use a LayoutBuilder to define the size of the BlockWidgets.
3.4 BlockWidget
class BlockWidget extends StatelessWidget {
final Block block;
final double size;
const BlockWidget({
Key key,
this.block,
this.size,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizedBox(
width: size,
height: size,
child: Padding(
padding: EdgeInsets.all(size / 10),
child: Container(
decoration: BoxDecoration(
color: block.color,
border: Border.all(color: Colors.black87, width: size / 20),
borderRadius: BorderRadius.circular(size / 15),
),
),
),
);
}
}
Another basic StatelessWidget.
Full Application Code
Just copy-paste the following to try it out.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
part '66053795.shuffle.freezed.dart';
Random random = Random();
void main() => runApp(ProviderScope(child: AppWidget()));
class AppWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.amber,
accentColor: Colors.black87,
),
home: HomePage(),
);
}
}
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final rack = useProvider(RackStateNotifier.provider.state);
return Scaffold(
appBar: AppBar(
title: Row(
children: const [
Icon(Icons.casino_outlined),
SizedBox(
width: 8.0,
),
Text('Rack Shuffler'),
],
),
),
body: Center(
child: RackWidget(rack: rack),
),
bottomNavigationBar: BottomAppBar(
color: Theme.of(context).primaryColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
icon: Icon(Icons.refresh),
iconSize: 48,
onPressed: () => context.read(RackStateNotifier.provider).deal(),
),
IconButton(
icon: Icon(Icons.shuffle),
iconSize: 48,
onPressed: () =>
context.read(RackStateNotifier.provider).shuffle(),
),
],
),
),
);
}
}
class RackWidget extends StatelessWidget {
final Rack rack;
const RackWidget({Key key, this.rack}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: LayoutBuilder(
builder: (context, constraints) {
return Row(
children: rack.blocks
.map((block) => BlockWidget(
block: block,
size: constraints.biggest.width / rack.blocks.length))
.toList(),
);
},
),
);
}
}
class BlockWidget extends StatelessWidget {
final Block block;
final double size;
const BlockWidget({
Key key,
this.block,
this.size,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizedBox(
width: size,
height: size,
child: Padding(
padding: EdgeInsets.all(size / 10),
child: Container(
decoration: BoxDecoration(
color: block.color,
border: Border.all(color: Colors.black87, width: size / 20),
borderRadius: BorderRadius.circular(size / 15),
),
),
),
);
}
}
class RackStateNotifier extends StateNotifier<Rack> {
static final provider =
StateNotifierProvider<RackStateNotifier>((ref) => RackStateNotifier());
RackStateNotifier([Rack state]) : super(state ?? Rack.create());
void shuffle() {
state = state.shuffled;
}
void deal() {
state = Rack.create();
}
}
#freezed
abstract class Block with _$Block {
const factory Block({Color color}) = _Block;
}
#freezed
abstract class Rack implements _$Rack {
const factory Rack({List<Block> blocks}) = _Rack;
const Rack._();
static Rack create([int nbBlocks]) => Rack(
blocks: List.generate(
nbBlocks ?? 4 + random.nextInt(6),
(index) => Block(
color: Color(0x66000000 + random.nextInt(0xffffff)),
),
),
);
Rack get shuffled => Rack(blocks: blocks..shuffle());
}
Here is a solution using a GlobalKey.
It feels pretty inelegant. It surprises me that with the close relationship between the widget and its state object, there's no easy way for a widget's method to call a method on the SO. The "widget.blah" construct provides a way for the SO to access the widget's data, is there a reason for not having a similar "state.myMethod" construct?
Anyway, the following works:
import 'package:flutter/material.dart';
List<Block> g_blocks = [Block(Colors.red), Block(Colors.green),
Block(Colors.blue), Block(Colors.purple)];
GlobalKey g_key = GlobalKey();
Rack g_rack = new Rack(key: g_key);
void main() => runApp(MyApp());
// This widget is the root of your application.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
fontFamily: 'PressStart',
),
home: MyHomeScreen(),
);
}
}
class MyHomeScreen extends StatefulWidget {
MyHomeScreen({Key key}) : super(key: key);
createState() => MyHomeScreenState();
}
class MyHomeScreenState extends State<MyHomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: Text('Thanks for your help')),
backgroundColor: Colors.pink,
),
body: Center(
child: g_rack,
),
bottomNavigationBar: SizedBox(
height: 100.0,
child: BottomNavigationBar(
currentIndex: 0,
iconSize: 48.0,
backgroundColor: Colors.lightBlue[100],
items: [
BottomNavigationBarItem(
label: 'Shuffle',
icon: Icon(Icons.home),
),
BottomNavigationBarItem(
label: 'Shuffle',
icon: Icon(Icons.home),
),
],
onTap: (int index) {
g_blocks.shuffle();
g_key.currentState.setState(() {
});
}
),
),
);
} // build
} // End class MyHomeScreenState
class Rack extends StatefulWidget {
Rack({Key key}) : super(key: key);
#override
_rackState createState() => _rackState();
}
class _rackState extends State<Rack> {
#override
Widget build(BuildContext context) {
return Container(
height: 150.0,
color: Colors.yellow[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: g_blocks),
);
}
void updateRack(){
setState(() {});
}
}
class Block extends StatelessWidget {
final Color color;
Block(this.color);
#override
Widget build(BuildContext context) {
return Container(height:50,width:50, color: color,);
}
}

Flutter Navdrawer disappears when I navigate from screen within screen

When I click on the drawer item (homepage) it works fine and I can access the drawer from appbar of homepage screen. Now the problem is when I navigate from the homepage to another screen (beginner plan),
the drawer disappears.
Navdrawer.dart code
import 'package:fitnessapp/components/constants.dart';
import 'package:fitnessapp/screens/NavPages/beginnerplan.dart';
import 'package:fitnessapp/screens/NavPages/running.dart';
import 'NavPages/homepage.dart';
import 'NavPages/profile.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:flutter/material.dart';
class DrawerItem {
String title;
IconData icon;
DrawerItem(this.title, this.icon);
}
class navdrawer extends StatefulWidget {
final drawerItems = [
new DrawerItem("Home Page", Icons.home),
new DrawerItem("Profile", Icons.account_circle),
new DrawerItem("Running", Icons.directions_run),
];
#override
State<StatefulWidget> createState() {
return new navdrawerState();
}
}
class navdrawerState extends State<navdrawer> {
int _selectedDrawerIndex = 0;
navdrawerState();
_getDrawerItemWidget(int pos) {
switch (pos) {
case 0:
return new homepage();
case 1:
return new profilepage();
case 2:
return new RunningPage();
default:
return Container();
}
}
onSelectItem(int index) {
setState(() => _selectedDrawerIndex = index);
Navigator.of(context).pop(); // close the drawer
}
#override
Widget build(BuildContext context) {
var drawerOptions = <Widget>[];
for (var i = 0; i < widget.drawerItems.length; i++) {
var d = widget.drawerItems[i];
drawerOptions.add(new ListTile(
leading: new Icon(d.icon),
title: new Text(d.title),
selected: i == _selectedDrawerIndex,
onTap: () => onSelectItem(i),
));
}
return new Scaffold(
appBar: new AppBar(
// here we display the title corresponding to the fragment
// you can instead choose to have a static title
title: new Text(widget.drawerItems[_selectedDrawerIndex].title),
),
drawer: new Drawer(
child: Column(
children: [
Expanded(flex: 1, child: UserHeader()),
Expanded(
flex: 2,
child: new Column(children: drawerOptions),
)
],
),
),
body: _getDrawerItemWidget(_selectedDrawerIndex),
);
}
}
class UserHeader extends StatelessWidget {
const UserHeader({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: mainaccent,
width: MediaQuery.of(context).size.width * 0.85,
child: DrawerHeader(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("images/minerva_tp.png"),
fit: BoxFit.cover)),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(
height: 50,
),
Text(
"Shahroz Javed",
style: TextStyle(color: Colors.white,fontSize: 20),
),
Text(
"shahrozjaved39#gmail.com",
style: TextStyle(color: Colors.white,fontSize: 15),
),
],
),
),
);
}
}
homepage.dart code for navgiation to biggenerplan
ontap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => beginnerplan()),
);
beginnerplan.dart code
import 'package:fitnessapp/screens/Navdrawer.dart';
import 'package:flutter/material.dart';
class beginnerplan extends StatefulWidget {
#override
_beginnerplanState createState() => _beginnerplanState();
}
class _beginnerplanState extends State<beginnerplan> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text("Beginner plan"),
);
}
}

How change the Icon of an IconButton when it is pressed

I want to know how I can change the Icon of an IconButton when it is pressed. (Favorite_border to Favorite). I tried somethings but it doesn't works.
Maybe it is easy but I am a beginner and I don't understand very well how it is works.
Update
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import '../recyclerview/data.dart';
import 'package:watch/constants.dart';
int itemCount = item.length;
List<bool> selected = new List<bool>();
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
initState() {
for (var i = 0; i < itemCount; i++) {
selected.add(false);
}
super.initState();
}
Icon notFavorite = Icon(Icons.favorite_border, size: 25,);
Icon inFavorite = Icon(Icons.favorite, size: 25,);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Accueil', style: kAppBarStyle,),
//backgroundColor: Colors.white,
elevation: 0,
),
body: ListView.builder(
itemCount: itemCount,
itemBuilder: (BuildContext context, int index) {
return Container(
child: new Row(
children: <Widget>[
//Image
new Container(
margin: new EdgeInsets.all(5.0),
child: new CachedNetworkImage(
imageUrl: item[index].imageURL,
height: MediaQuery.of(context).size.width / 4,
width: MediaQuery.of(context).size.width / 2,
fit: BoxFit.cover,
),
),
//Text
Expanded(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Spacer(),
//Titre
Container(
padding: const EdgeInsets.only(bottom: 75.0, top: 10.0 ),
child: Text(
item[index].title,
style: kItemTitle,
),
),
//Decription
Container(
padding: const EdgeInsets.only(left: 10.0, top: 10.0),
child:Text(
item[index].description,
style: kItemDescription,
),
),
//Favoris
Spacer(),
GestureDetector(
child: Container(
padding: const EdgeInsets.only(right: 10.0, top: 3.0),
child: selected.elementAt(index) ? inFavorite : notFavorite,
),
onTap: () {
setState(() {
selected[index] = !selected.elementAt(index);
});
},
),
],
),
),
],
),
);
}
)
);
}
}
It is a ListView with Images, Texts and the Favorite Button and it works fine.
First you need a boolean variable.
bool toggle = false;
After that you can use IconButton like this:
IconButton(
icon: toggle
? Icon(Icons.favorite_border)
: Icon(
Icons.favorite,
),
onPressed: () {
setState(() {
// Here we changing the icon.
toggle = !toggle;
});
}),
custom radio button (some IconButton in ListView that change their icons):
main.dart file :
import 'package:flutter/material.dart';
import 'my_home_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
my_home_page.dart file:
import 'package:flutter/material.dart';
int itemCount = 5;
List<bool> selected = new List<bool>();
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
initState() {
for (var i = 0; i < itemCount; i++) {
selected.add(false);
}
super.initState();
}
Icon firstIcon = Icon(
Icons.radio_button_on, // Icons.favorite
color: Colors.blueAccent, // Colors.red
size: 35,
);
Icon secondIcon = Icon(
Icons.radio_button_unchecked, // Icons.favorite_border
color: Colors.grey,
size: 35,
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ListView.builder(
itemCount: itemCount,
itemBuilder: (BuildContext context, int index) {
return IconButton(
icon: selected.elementAt(index) ? firstIcon : secondIcon,
onPressed: () {
try {
// your code that you want this IconButton do
setState(() {
selected[index] = !selected.elementAt(index);
});
print('tap on ${index + 1}th IconButton ( change to : ');
print(selected[index] ? 'active' : 'deactive' + ' )');
} catch (e) {
print(e);
}
},
);
}),
),
);
}
}
Copy paste the code and it will work :)
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
home: HomeApp(),
);
}
}
class HomeApp extends StatefulWidget {
#override
_HomeAppState createState() => _HomeAppState();
}
class _HomeAppState extends State<HomeApp> {
// Using a Bool
bool addFavorite = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter App :)"),
),
body: Center(
child: IconButton(
icon: Icon(addFavorite ? Icons.favorite : Icons.favorite_border),
onPressed: () {
// Setting the state
setState(() {
addFavorite = !addFavorite;
});
}),
),
);
}
}
Updating the Code for ListView
class _HomeAppState extends State<HomeApp> {
// Using a Bool List for list view builder
List<bool> addFavorite = List<bool>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter App :)"),
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
// Setting a bool initially
addFavorite.add(false);
return IconButton(
icon: Icon(addFavorite.elementAt(index)
? Icons.favorite
: Icons.favorite_border),
onPressed: () {
// Setting the state
setState(() {
// Changing icon of specific index
addFavorite[index] =
addFavorite[index] == false ? true : false;
});
});
}),
);
}
}
the IconButton must be in StatefulWidget and use a flag for unselected icon and selected icon:
.
.
.
bool selected = false;
Icon first_icon = Icon(...);
Icon second_icon = Icon(...);
.
.
.
IconButton(
icon: selected
? first_icon
: second_icon,
onPressed: () {
try {
// your code that you want this IconButton do
setState(() {
selected = !selected;
});
} catch(e) {
print(e);
}
}),
for use in ListView:
.
.
.
List<bool> selected = new List<bool>();
Icon first_icon = Icon(...);
Icon second_icon = Icon(...);
.
.
.
ListView.builder(
controller: scrollController,
primary: true,
...
itemCount: _yourListViewLength,
itemBuilder: (BuildContext context, int i) {
selected.add(false);
IconButton(
icon: selected.elementAt(i)
? first_icon
: second_icon,
onPressed: () {
try {
// your code that you want this IconButton do
setState(() {
selected.elementAt(i) = !selected.elementAt(i);
});
} catch(e) {
print(e);
}
}),
},
)
i hope this help you
My code if you want : home_screen.dart
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import '../recyclerview/data.dart';
import 'package:watch/constants.dart';
class ListViewExample extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new ListViewExampleState();
}
}
class ListViewExampleState extends State<ListViewExample>{
bool addFavorite = false;
Icon notFavorite = Icon(Icons.favorite_border, size: 25,);
Icon inFavorite = Icon(Icons.favorite, size: 25,);
List<Container> _buildListItemsFromItems(){
return item.map((item){
var container = Container(
child: new Row(
children: <Widget>[
//Image
new Container(
margin: new EdgeInsets.all(5.0),
child: new CachedNetworkImage(
imageUrl: item.imageURL,
height: MediaQuery.of(context).size.width / 4,
width: MediaQuery.of(context).size.width / 2,
fit: BoxFit.cover,
),
),
//Text
Expanded(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Spacer(),
//Titre
Container(
padding: const EdgeInsets.only(bottom: 75.0, top: 5.0 ),
child: Text(
item.title,
style: kItemTitle,
),
),
//Decription
Container(
padding: const EdgeInsets.only(left: 10.0, top: 5.0),
child:Text(
item.description,
style: kItemDescription,
),
),
//Favoris
Spacer(),
GestureDetector(
child: Container(
padding: const EdgeInsets.only(right: 10.0, top: 1.0),
child: addFavorite ? inFavorite : notFavorite,
),
onTap: () {
setState(() {
addFavorite = !addFavorite;
});
},
),
],
),
),
],
),
);
return container;
}).toList();
}
//Scaffold Global
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Accueil', style: kAppBarStyle,),
//backgroundColor: Colors.white,
elevation: 0,
),
body: ListView(
children: _buildListItemsFromItems(),
),
);
}
}
It is not an IconButton but just an Icon but it is working.

How to populate a form from a listview on tap in flutter

I have a form widget, a list widget, and a "wrapper" widget or in other words, a parent/container widget. So to give an idea of the widget tree, it is as such.
Parent/Container Widget
Form Widget
Button Widget
List Widget
Notice that the form, buttons and list widget are all siblings, inside of the parent/container widget. What I want to happen, is tap on a list item in the list widget, and populate the form widget with the data that gets passed from the list widget.
Here is my parent widget.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:andplus_flutter_7_gui/services/user_service.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'crud_form.dart';
import 'crud_list.dart';
class Crud extends StatefulWidget {
Crud({Key key, this.title}) : super(key: key);
final String title;
_CrudContainerState createState() => _CrudContainerState();
}
class _CrudContainerState extends State<Crud> {
List<User> users;
User user = User();
UserService userService;
#override
void initState() {
super.initState();
if (userService == null) {
userService = UserService(user);
}
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
userService.dispose();
}
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text(widget.title),
),
body: Builder(
builder: (context) => Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 2,
child: StreamBuilder(
builder: (context, AsyncSnapshot<User> snapshot) {
return CrudForm(
user: snapshot.data,
onUserAdded: (user) {
userService.addUser(user);
},
);
},
stream: userService.userObservable,
),
),
Expanded(
child: Text("Future button widget"),
),
Expanded(
flex: 3,
child: StreamBuilder(
builder: (ctx, AsyncSnapshot<List<User>> snap) {
return CrudList(
onUserSelected: userService.userSelected,
users: snap.data,
);
},
stream: userService.usersObservable,
),
),
],
),
),
),
);
}
void onEditUser(User user) {
setState(() {
user = user;
});
}
}
The above widget wraps the three widgets I mentioned.
Here are the children widget:
Form:
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:flutter/material.dart';
class CrudForm extends StatefulWidget {
CrudForm({Key key, this.onUserAdded, this.user}) : super(key: key);
final User user;
final void Function(User user) onUserAdded;
_CrudFormState createState() => _CrudFormState(user: user);
}
class _CrudFormState extends State<CrudForm> {
_CrudFormState({this.user});
User user = User();
var _key = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Container(
child: Builder(
builder: (context) => Container(
color: Colors.blueAccent[100],
child: Form(
key: _key,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Text(
"First Name",
style: TextStyle(fontSize: 20),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
initialValue: widget.user?.firstName == null ||
widget.user.firstName.isEmpty
? user.firstName
: widget.user.firstName,
validator: (value) {
if (value.isEmpty) {
return "First name is required";
}
return null;
},
onSaved: (value) {
setState(() {
user.firstName = value;
});
},
),
),
)
],
),
Row(
children: <Widget>[
Text(
"Last Name",
style: TextStyle(fontSize: 20),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
validator: (value) {
if (value.isEmpty) {
return "Last name is required";
}
return null;
},
onSaved: (value) {
setState(() {
user.lastName = value;
});
},
),
),
),
],
),
RaisedButton(
child: Text(
"Save",
),
splashColor: Colors.blueGrey,
onPressed: () {
if (!_key.currentState.validate()) {
return;
}
_key.currentState.save();
widget.onUserAdded(
new User(
firstName: user.firstName,
lastName: user.lastName,
),
);
},
)
],
),
),
),
),
),
);
}
}
Here is my list widget.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:flutter/material.dart';
class CrudList extends StatefulWidget {
CrudList({Key key, this.users, this.onUserSelected}) : super(key: key);
final List<User> users;
final SelectUser onUserSelected;
_CrudListState createState() => _CrudListState();
}
class _CrudListState extends State<CrudList> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
child: ListView.builder(
itemCount: widget.users?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
var user = widget.users[index];
return ListTile(
key: Key(index.toString()),
title: Center(
child: Text(
"${user.firstName} ${user.lastName}",
style: TextStyle(color: Colors.white),
),
),
onTap: () {
print("${widget.users[index]} $index");
widget.onUserSelected(widget.users[index]);
},
);
},
),
);
}
}
typedef void SelectUser(User user);
And just for further context, here is my user service, responsible for adding the objects to the stream, and using the stream builder within rxdart to notify of state changes.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:rxdart/rxdart.dart';
class UserService {
User _editedUser = User();
List<User> _users = <User>[];
BehaviorSubject<User> _userSubject;
BehaviorSubject<List<User>> _usersSubject;
UserService(this._editedUser) {
_userSubject = BehaviorSubject<User>.seeded(_editedUser);
_usersSubject = BehaviorSubject<List<User>>.seeded(_users);
}
Observable<List<User>> get usersObservable => _usersSubject.stream;
Observable<User> get userObservable => _userSubject.stream;
addUser(User user) {
_users.add(user);
_usersSubject.add(_users);
}
dispose() {
_userSubject.close();
_usersSubject.close();
}
void userSelected(User user) {
_editedUser = user;
_userSubject.add(_editedUser);
}
}
What am I missing? It looks like my widget rebuilds, and tries to set the initial value in the form when I tap the user in the list widget. But the actual field doesn't get updated and I'm not sure why.
I'd appreciate any documentation or articles on how to better approach data and state management between sibling widgets within the flutter framework.
Here's a similar use case that I tried to implement locally. What I'm doing here is I generate TextFormFields dynamically and assign TextEditingController.
Column textField(int n) {
List<Widget> listForm = [];
while (n > 0) {
var textEditingController = TextEditingController();
listForm.add(
TextFormField(
controller: textEditingController,
onTap: () {
_selectedField = textEditingController;
},
),
);
n--;
}
return Column(children: listForm);
}
Clicking a ListView item updates the text of the currently selected TextFormField.
InkWell(
onTap: () {
debugPrint('Selected $index!');
if (_selectedField != null) {
_selectedField!.value = TextEditingValue(text: 'Item $index');
}
},
child: ListTile(
title: Text('Item $index'),
),
);
Complete sample
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController? _selectedField;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(flex: 1, child: textField(3)),
Expanded(flex: 1, child: listItems()),
],
),
),
),
);
}
Column textField(int n) {
List<Widget> listForm = [];
while (n > 0) {
var textEditingController = TextEditingController();
listForm.add(
TextFormField(
controller: textEditingController,
onTap: () {
debugPrint('Current Controller: $textEditingController');
_selectedField = textEditingController;
},
),
);
n--;
}
return Column(children: listForm);
}
ListView listItems() {
return ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
if (_selectedField != null) {
_selectedField!.value = TextEditingValue(text: 'Item $index');
}
},
child: ListTile(
title: Text('Item $index'),
),
);
},
);
}
}
Demo