Controll flutter pageview widgets or variables for one page only - flutter

How to control specific widget to hide or to show in pageview. In PageView Widget in flutter for specific page only, how we can control over widget to be hidden or shown based on bool value. how we can utilize pageview controller to do this

you can change the children in the widget tree depending on a boolean value.
Example: child: condition ? WidgetWhenTrue : WidgetWhenFalse
UPDATE
The best way I found is, that you create your pages and page children dynamicly.
You could provide a List<Widget> which will represent the max. Content and then remove the widget you don't want to have.
Or you could add the widgets on the fly to the pages.
import 'package:flutter/material.dart';
class PageViewWidget extends StatefulWidget {
#override
_PageViewWidgetState createState() => _PageViewWidgetState();
}
class _PageViewWidgetState extends State<PageViewWidget> {
PageController _pageController;
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
List<Text> maxContent = [
Text('text 1'),
Text('text 2'),
Text('text 3'),
Text('text 4'),
Text('text 5'),
Text('text 6')
];
bool condition = true;
Container dynamicPageChildren({Color color, List<int> delPos}) {
Container newPage;
List<Widget> newContent = List.from(maxContent);
// modify your Widget List
print('length = ${maxContent.length} ');
for (int i in delPos.reversed) {
// use reversed or provide the last elemt to remove first if not,
// your list will shrink and the element you want to remove last does not exist or is the wrong one
print('delete at pos $i');
newContent.removeAt(i);
}
// add it to the page
newPage = Container(
color: color,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: newContent));
return newPage;
}
List<Container> dynamicPages({List<Color> colorList}) {
// you could also pass the index positions into this function
// or call your logic to decide which index should not be displayed
List<Container> newPageList = [];
// modify your Widget List
int i = 0;
for (Color color in colorList) {
// example with given indices
// newPageList.add(dynamicPageChildren(color: color, delPos: [1, 3, 5]));
newPageList.add(dynamicPageChildren(color: color, delPos: [i]));
i = i + 1;
}
return newPageList;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: PageView(
controller: _pageController,
children: dynamicPages(
colorList: [Colors.red, Colors.orange, Colors.yellow])),
),
);
}
}

Related

Flutter Wrap widget rendering glitches when height is updated

I am creating a form (not using the Form Widget) in Flutter where the user can add an arbitrary amount of items (treatments) which are rendered as InputChip widgets list in a Wrap widget.
The form uses a button (AddButton widget) which opens a form dialog which itself returns the newly created item (treatment) that is added to selectedItems:
class TreatmentsWidget extends StatefulWidget {
const TreatmentsWidget({super.key, required this.selectedItems});
final List<Treatment> selectedItems;
#override
State<TreatmentsWidget> createState() => _TreatmentsWidgetState();
}
class _TreatmentsWidgetState extends State<TreatmentsWidget> {
#override
Widget build(BuildContext context) {
var chips = widget.selectedItems.map(
(item) {
return InputChip(
label: Text('${item.name} - ${item.frequency}/${item.frequencyUnit.name})',
);
},
).toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
children: chips,
),
AddButton(onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return const TreatmentDialog();
}).then((value) {
if (value != null) {
Treatment item = value;
setState(() {
widget.selectedItems.add(item);
});
}
});
}),
],
);
}
}
For some reason, when a new item is added to selectedItem and that the item overflows the current line, the layout is not recomputed such that the Wrap widget overflows the button:
However, as soon as the user scroll (the whole screen content is inside a SingleChildScrollView), the layout is recomputed and the Wrap takes the right amount of space:
How can I force a redraw when a new item is added to prevent this glitch?
The issue seems to be that the Column does not recompute its size on the current frame when one of his child size changes.
I ended up forcing rebuilding the Column using a ValueKey whenever chips.length changes:
class _TreatmentsWidgetState extends State<TreatmentsWidget> {
#override
Widget build(BuildContext context) {
var chips = ...;
return Column(
key: ValueKey(chips.length),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
children: chips,
),
AddButton(...),
],
);
}
}
I am still interested in a cleaner solution if it exists. Using a provider seems overkill for this case.

Flutter SetState from custom widget (or more down the line)

I have a simple flutter app. It has a stateful widget in the Home Page with a simple Scaffold which only has a Column. One of the children in the column is a custom widget called buildBodyLayout(). This widget has its own column with some Text widgets, and another custom widget called buildButton(). This new widget has a button which needs to setState of a variable in the Home view. I pass the value of the variable when calling the widget. But each widget is in its own dart file since I am re-using the same widget in other pages.
How do I setState the main stateful widget from inside custom widgets?
If I write everything inside the same page, it all works fine. How do I use a widget in a different dart file to set the sate of a parent widget?
Sample Code
Home Stateful Widget
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int changeValue;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("Welcome to my App"),
Text("The Change Value is: $changeValue"),
buildBodyLayout(changeValue),
],
),
);
}
}
buildBodyLayouot Widget
Widget buildBodyLayout(int value){
return Column(
children: [
Text("Press the + and - Buttons to change Value"),
buildButtons(value),
],
);
buildButtons Widget
Widget buildButtons(int value){
return Column(
children: [
RaisedButton(
child: Text("Increase Value"),
onPressed: (){
value = value + 1; //THIS SHOULD SET STATE
}) ,
RaisedButton(
child: Text("Decrease Value"),
onPressed: (){
value = value - 1; //THIS SHOULD SET STATE
})
],
);
}
}
Thank you for any help!
Widgets in flutter are classes that extend the widget(StatefulWidget, StatelessWidget) class.
In your code your using functions to build your widgets, which is not recommended.
You can see the difference between class widgets and function widgets here:
What is the difference between functions and classes to create reusable widgets?
Aside from that, using function or classes, to solve your problem you need to use a callback.
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int changeValue = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("Welcome to my App"),
Text("The Change Value is: $changeValue"),
buildBodyLayout(changeValue,
addToValue: (int increment){
setState((){
changeValue += increment;
});
}
),
],
),
);
}
}
Widget buildBodyLayout(int value, Function(int newValue) addToValue){
return Column(
children: [
Text("Press the + and - Buttons to change Value"),
buildButtons(value, addToValue),
],
);
}
Widget buildButtons(int value, Function(int newValue) addToValue){
return Column(
children: [
RaisedButton(
child: Text("Increase Value"),
onPressed: (){
addToValue(1);
}),
RaisedButton(
child: Text("Decrease Value"),
onPressed: (){
addToValue(-1);
})
],
);
}
You also don't need to put your widgets in different files to reuse them, but it's recommended that you do that.

How to expand a widget in ListView ontap?

I have created a ListView with container boxes as widgets. I want a specific container to expand onTap upto a specific screen height and width. I need help in implementing this in flutter. I have made a prototype on AdobeXD.
AdobeXD Prototype GIF
I am new to flutter, any kind of help is appreciated.
A flutter plugin called flutter swiper might help you achieve what you want to achieve.
Visit this pub dev and you can read documentation.
Here you go brother, Although its not blurring the background but I think it will get you going.
It's working something like this:
Below the code which you can copy paste. I have added comments in the code for understanding it in better way. Cheers :)
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeApp(),
);
}
}
class HomeApp extends StatefulWidget {
#override
_HomeAppState createState() => _HomeAppState();
}
class _HomeAppState extends State<HomeApp> {
// Items in the list --> Custom Widgets
List<Widget> arr = [
ListContainerHere(),
ListContainerHere(),
ListContainerHere(),
ListContainerHere(),
ListContainerHere(),
ListContainerHere(),
];
Widget getListWidget(List<Widget> items) {
List<Widget> list = new List<Widget>();
for (var i = 0; i <= items.length; i++) {
list.add(new ListContainerHere(
index: i,
));
}
return Row(children: list);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter App :)"),
),
body: Center(
// Using a 'Row' as Horizontal ListView
child: SingleChildScrollView(
scrollDirection: Axis.horizontal, child: getListWidget(arr)),
),
);
}
}
// Widgets that will be rendered in the Horizontal Row
class ListContainerHere extends StatefulWidget {
final int index;
ListContainerHere({this.index});
#override
_ListContainerHereState createState() => _ListContainerHereState();
}
class _ListContainerHereState extends State<ListContainerHere> {
// Varibale to change the height and width accordingly
// Initally no item will be expanded
bool isExpanded = false;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
// Changing the value of 'isExpanded' when an item is tapped in the List
setState(() {
isExpanded = !isExpanded;
});
},
// AnimatedContainer for slowing down the changing
child: AnimatedContainer(
duration: Duration(milliseconds: 150),
// Changing the width and height
height: isExpanded ? 250 : 150,
width: isExpanded ? 250 : 150,
// Decoration Portion of the Container
decoration: BoxDecoration(
color: Colors.blue, borderRadius: BorderRadius.circular(15.0)),
),
),
);
}
}

How can I create staggered grid view like this in flutter

If you need more info then please comment.Thank you
How can I create flutter staggered grid view like
this with headerTile + image in each grid tile + centered texts
First, create a child and parent model class, ParentModel contains a header text as well list of its children,
class ParentModel {
String title;
List<ChildModel> list;
ParentModel(this.title, this.list);
}
class ChildModel {
String text;
ChildModel(this.text);
}
Then create a ListView, this list will contain title as well as a grid of its children.
class ComplexList extends StatefulWidget {
#override
_ComplexListState createState() => _ComplexListState();
}
class _ComplexListState extends State<ComplexList> {
List<ParentModel> parentList = List();
#override
void initState() {
super.initState();
// this list is just to add dummy data, replace this with your list from api
List<ChildModel> childList = List();
childList.add(ChildModel('Child1'));
childList.add(ChildModel('Child2'));
childList.add(ChildModel('Child3'));
childList.add(ChildModel('Child4'));
List<ChildModel> childList1 = List();
childList1.add(ChildModel('Child5'));
childList1.add(ChildModel('Child6'));
childList1.add(ChildModel('Child7'));
childList1.add(ChildModel('Child8'));
parentList.add(ParentModel('Title1', childList));
parentList.add(ParentModel('Title2', childList1));
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: parentList.length,
itemBuilder: (context, index) {
ParentModel parentModel = parentList[index];
return Column(
children: <Widget>[Text('${parentModel.title}',style: TextStyle(fontSize: 16),),
GridView.count(
shrinkWrap: true,
// Create a grid with 2 columns. If you change the scrollDirection to
// horizontal, this produces 2 rows.
crossAxisCount: 2,
// Generate 100 widgets that display their index in the List.
children: List.generate(parentModel.list.length, (index) {
ChildModel childModel = parentModel.list[index];
return Card(
child: Center(
child: Text(
'Item ${childModel.text}',
style: Theme
.of(context)
.textTheme
.headline,
),
),
);
}),
),
],
);
});
}
}
The output of this code,

Fix last element of ListView to the bottom of screen

I am trying to implement a custom navigation drawer using Flutter. I would like to attach log out option to the bottom of the drawer. The problem is that number of elements above log out option is unknow (from 3 to 17).
So if these widgets take half of the space of a drawer, then log out option will be on the bottom, and if there is too much of them and you have to scroll to see them all, then the log out option will be simply the last.
I am also trying to give the first two options a green background color. Which widget tree would you recommend me? I had a thought about the ListView widget, it takes List of widgets as an argument in constructor.
Therefore I can solve the different background color for the first two items. But I still can't figure out how to attach the log out option to the bottom. In this case it's at the bottom of drawer, but it can happen, that other options will be bigger than screen size and in that case, it should be placed at the bottom of whole list.
EDIT: I've add a design to the question. The logout option is the one called Odhlášení. In this case it's at the bottom of drawer, but it can happen, that other options will be bigger than the screen size and in that case, it should be placed at the bottom of whole list.
Design:
You can simply use ListView to manage the "17" navigation options. Wrap this ListView inside an Column. The ListView will be the first child of the Column the second child, therefore placing at the bottom, will be your logout action.
If you are using transparent widgets (like ListTile) inside your ListView to display the navigation options, you can simply wrap it inside a Container. The Container, besides many other widgets, allows you to set a new background color with its color attribute.
Using this approach the widget tree would look like the following:
- Column // Column to place your LogutButton always below the ListView
- ListView // ListView to wrap all your navigation scrollable
- Container // Container for setting the color to green
- GreenNavigation
- Container
- GreenNavigation
- Navigation
- Navigation
- ...
- LogOutButton
Update 1 - Sticky LogOutButton :
To achieve the LogOutButton sticking to the end of the ListView you'll neeed to do two things:
Replace the Expanded with an Flexible
Set shrinkWrap: true inside the ListView
Update 2 - Spaced LogOutButton until large List:
Achieving the described behavior is a more difficult step. You'll have to check if the ListView exceeds the screen and is scrollable.
To do this I wrote this short snippet:
bool isListLarge() {
return controller.positions.isNotEmpty && physics.shouldAcceptUserOffset(controller.position);
}
It will return true if the ListView exceeds its limitations. Now we can refresh the state of the view, depending on the result of isListViewLarge. Below again a full code example.
Standalone code example (Update 2: Spaced LogOutButton until large List):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
),
);
}
}
class MyDrawer extends StatefulWidget {
#override
_MyDrawerState createState() => _MyDrawerState();
}
class _MyDrawerState extends State<MyDrawer> {
ScrollController controller = ScrollController();
ScrollPhysics physics = ScrollPhysics();
int entries = 4;
#override
Widget build(BuildContext context) {
Widget logout = IconButton(
icon: Icon(Icons.exit_to_app),
onPressed: () => {setState(() => entries += 4)});
List<Widget> navigationEntries = List<int>.generate(entries, (i) => i)
.map<Widget>((i) => ListTile(
title: Text(i.toString()),
))
.toList();
if (this.isListLarge()) { // if the List is large, add the logout to the scrollable list
navigationEntries.add(logout);
}
return Drawer(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // place the logout at the end of the drawer
children: <Widget>[
Flexible(
child: ListView(
controller: controller,
physics: physics,
shrinkWrap: true,
children: navigationEntries,
),
),
this.isListLarge() ? Container() : logout // if the List is small, add the logout at the end of the drawer
],
),
);
}
bool isListLarge() {
return controller.positions.isNotEmpty && physics.shouldAcceptUserOffset(controller.position);
}
}
Standalone code example (Update 1: Sticky LogOutButton):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
),
);
}
}
class MyDrawer extends StatefulWidget {
#override
_MyDrawerState createState() => _MyDrawerState();
}
class _MyDrawerState extends State<MyDrawer> {
int entries = 4;
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: <Widget>[
Flexible(
child: ListView(
shrinkWrap: true,
children: List<int>.generate(entries, (i) => i)
.map((i) => ListTile(
title: Text(i.toString()),
))
.toList(),
),
),
IconButton(
icon: Icon(Icons.exit_to_app),
onPressed: () => {setState(() => entries += 4)})
],
),
);
}
}
Standalone code example (Old: Sticking to bottom):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
),
);
}
}
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: <Widget>[
Expanded(
child: ListView(
children: List<int>.generate(40, (i) => i + 1)
.map((i) => ListTile(
title: Text(i.toString()),
))
.toList(),
),
),
IconButton(icon: Icon(Icons.exit_to_app), onPressed: () => {})
],
),
);
}
}