How conditional render children widgets inside a stack in flutter? - flutter

I want to keep the drawerscreen in default and render the other screens in if else condition . How can i do that ? , Thanks in advance for the help `import 'package:flutter/material.dart';
class Body extends StatelessWidget {
final String showScreen;
const Body({
Key key,
this.showScreen="map",
}) : super(key:key);
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
DrawerScreen(),
MapScreen(),
// PostScreen()
],
);
}
}`

you mean something like this?
Widget _conditionedWidget(){
if(condition)
return MapScreen();
else
return PostScreen();
}
and then:
return Stack(
children: <Widget>[
DrawerScreen(),
_conditionedWidget(),
],
);

Related

Navigate part of screen from drawer

let's say I have an app with the following setup:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.grey[200],
child: Row(
children: [
MainMenu(),
Expanded(child: MainLoginScreen()),
],
),
));
}
}
I would like to know how can I navigate only the MainLoginScreen widget from the MainMenu with any .push() method.
(I found a way to navigate from a context inside the mainloginscreen,by wrapping it with a MaterialApp widget, but what if I want to use the MainMenu widget instead, which has another context)
There is a general agreement that a 'screen' is a topmost widget in the route. An instance of 'screen' is what you pass to Navigator.of(context).push(MaterialPageRoute(builder: (context) => HereGoesTheScreen()). So if it is under Scaffold, it is not a screen. That said, here are the options:
1. If you want to use navigation with 'back' button
Use different screens. To avoid code duplication, create MenuAndContentScreen class:
class MenuAndContentScreen extends StatelessWidget {
final Widget child;
MenuAndContentScreen({
required this.child,
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.grey[200],
child: Row(
children: [
MainMenu(),
Expanded(child: child),
],
),
),
);
}
}
Then for each screen create a pair of a screen and a nested widget:
class MainLoginScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MenuAndContentScreen(
child: MainLoginWidget(),
);
}
}
class MainLoginWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Here goes the screen content.
}
}
2. If you do not need navigation with 'back' button
You may use IndexedStack widget. It can contain multiple widgets with only one visible at a time.
class MenuAndContentScreen extends StatefulWidget {
#override
_MenuAndContentScreenState createState() => _MenuAndContentScreenState(
initialContentIndex: 0,
);
}
class _MenuAndContentScreenState extends State<MenuAndContentScreen> {
int _index;
_MainMenuAndContentScreenState({
required int initialContentIndex,
}) : _contentIndex = initialContentIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.grey[200],
child: Row(
children: [
MainMenu(
// A callback that will be triggered somewhere down the menu
// when an item is tapped.
setContentIndex: _setContentIndex,
),
Expanded(
child: IndexedStack(
index: _contentIndex,
children: [
MainLoginWidget(),
SomeOtherContentWidget(),
],
),
),
],
),
),
);
}
void _setContentIndex(int index) {
setState(() {
_contentIndex = index;
});
}
}
The first way is generally preferred as it is declrative which is a major idea in Flutter. When you have the entire widget tree statically declared, less things can go wrong and need to be tracked. Once you feel it, it really is a pleasure. And if you want to avoid back navigation, use replacement as ahmetakil has suggested in a comment: Navigator.of(context).pushReplacement(...)
The second way is mostly used when MainMenu needs to hold some state that needs to be preserved between views so we choose to have one screen with interchangeable content.
3. Using a nested Navigator widget
As you specifically asked about a nested Navigator widget, you may use it instead of IndexedStack:
class MenuAndContentScreen extends StatefulWidget {
#override
_MenuAndContentScreenState createState() => _MenuAndContentScreenState();
}
class _MenuAndContentScreenState extends State<MenuAndContentScreen> {
final _navigatorKey = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.grey[200],
child: Row(
children: [
MainMenu(
navigatorKey: _navigatorKey,
),
Expanded(
child: Navigator(
key: _navigatorKey,
onGenerateRoute: ...
),
),
],
),
),
);
}
}
// Then somewhere in MainMenu:
final anotherContext = navigatorKey.currentContext;
Navigator.of(anotherContext).push(...);
This should do the trick, however it is a bad practice because:
MainMenu knows that a particular Navigator exists and it should interact with it. It is better to either abstract this knowledge with a callback as in (2) or do not use a specific navigator as in (1). Flutter is really about passing information down the tree and not up.
At some point you would like to highlight the active item in MainMenu, but it is hard for MainMenu to know which widget is currently in the Navigator. This would add yet another non-down interaction.
For such interaction there is BLoC pattern
In Flutter, BLoC stands for Business Logic Component. In its simpliest form it is a plain object that is created in the parent widget and then passed down to MainMenu and Navigator, these widgets may then send events through it and listen on it.
class CurrentPageBloc {
// int is an example. You may use String, enum or whatever
// to identify pages.
final _outCurrentPageController = BehaviorSubject<int>();
Stream<int> _outCurrentPage => _outCurrentPageController.stream;
void setCurrentPage(int page) {
_outCurrentPageController.sink.add(page);
}
void dispose() {
_outCurrentPageController.close();
}
}
class MenuAndContentScreen extends StatefulWidget {
#override
_MenuAndContentScreenState createState() => _MenuAndContentScreenState();
}
class _MenuAndContentScreenState extends State<MenuAndContentScreen> {
final _currentPageBloc = CurrentPageBloc();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.grey[200],
child: Row(
children: [
MainMenu(
currentPageBloc: _currentPageBloc,
),
Expanded(
child: ContentWidget(
currentPageBloc: _currentPageBloc,
onGenerateRoute: ...
),
),
],
),
),
);
}
#override
void dispose() {
_currentPageBloc.dispose();
}
}
// Then in MainMenu:
currentPageBloc.setCurrentPage(1);
// Then in ContentWidget's state:
final _navigatorKey = GlobalKey();
late final StreamSubscription _subscription;
#override
void initState() {
super.initState();
_subscription = widget.currentPageBloc.outCurrentPage.listen(_setCurrentPage);
}
#override
Widget build(BuildContext context) {
return Navigator(
key: _navigatorKey,
// Everything else.
);
}
void _setCurrentPage(int currentPage) {
// Can't use this.context, because the Navigator's context is down the tree.
final anotherContext = navigatorKey?.currentContext;
if (anotherContext != null) { // null if the event is emitted before the first build.
Navigator.of(anotherContext).push(...); // Use currentPage
}
}
#override
void dispose() {
_subscription.cancel();
}
This has advantages:
MainMenu does not know who will receive the event, if anybody.
Any number of listeners may listen on such events.
However, there is still a fundamental flaw with Navigator. It can be navigated without MainMenu knowledge using 'back' button or by its internal widgets. So there is no single variable that knows which page is showing now. To highlight the active menu item, you would query the Navigator's stack which eliminates the benefits of BLoC.
For all these reasons I still suggest one of the first two solutions.

Flutter, calling a function inside the button

I am new to flutter and I make some practices. I have a StatelessWidget called doChange and makeChange and one StatefulWidget. This class which is statefulwidget I made child of the home page of the app also. But, I think that it is unnecessary to define here. My purpose in this case is that, I want to change the state of the button make open,make closed and at the same time the text open and close will also change. I think that class changeText has not problem but in makeChange class I have some trouble with creating constructor and function to call into onPress. The states do not change. How can i solve this or is that any way to do this without function ?
class changeText extends StatelessWidget{
final doChange;
changeText({#required this.doChange});
#override
Widget build(BuildContext context){
return Container(
//some codes
//some codes
child: doChange ? Text("open") : Text("close"),
);
}
}
class makeChange extends StatelessWidget{
final changeState;
makeChange({#required this.changeState}); // I want to add constructor here lets say onPressButton
whenPressed(){ // I want to create a function with the that constructor that I have add.
}
#override
Widget build(BuildContext context){
return Container(
//some codes
//
child: Column(
children: [
MaterialButton(
//some codes
//
onPressed: () {} // Here I want to call a function when press the button.
child: changeState ? Text("make open") : Text("make close"),
),
],
),
);
}
}
class Mainarea extends StatefulWidget{
#override
_MainareaState createState() => _mainAreaState();
}
class _MainareaState extends State<Mainarea> {
bool isChange= false;
#override
Widget build(BuildContext context){
return Container(
//some codes
//
child: Column(
children: <Widget>[
changeText(doChange: !this.isChange),
makeChange(changeState: !this.isChange),
],
),
);
}
}
I just added a final Function(bool) callback as a parameter, which can be called inside from the stateless widget, and returns to the calling function. From there you can call setState
class changeText extends StatelessWidget {
final bool doChange;
changeText({#required this.doChange});
#override
Widget build(BuildContext context) {
return Container(
//some codes
//some codes
child: doChange ? Text("open") : Text("close"),
);
}
}
class makeChange extends StatelessWidget {
final bool changeState;
final Function(bool) callback;
makeChange(
{#required
this.changeState,
#required
this.callback}); // You can rename this to onPressed or whatever
#override
Widget build(BuildContext context) {
return Container(
//some codes
//
child: Column(
children: [
MaterialButton(
//some codes
//
onPressed: () => callback( changeState),
child: changeState ? Text("make close") : Text("make open"), //I had to swap these around to make the text correct
),
],
),
);
}
}
class Mainarea extends StatefulWidget {
#override
_MainareaState createState() => _MainareaState();
}
class _MainareaState extends State<Mainarea> {
bool isChange = false;
#override
Widget build(BuildContext context) {
return Container(
//some codes
//
child: Column(
children: <Widget>[
changeText(doChange: !this.isChange),
makeChange(
changeState: !this.isChange,
callback: (bool val) {
setState(() => isChange = val); //this is your function that returns and resetst the value in the parent widget
},
),
],
),
);
}
}

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 to pass the widget as a parameter to the function

I want to pass some widgets as a parameter to function, does flutter support that? Below is my code. Should I pass a widget as a parameter to function?
This is the widget that I want to pass as a parameter:
class FirstWidget extends StatelessWidget {
#override
Widget build(BuildContext context){
return Container(
child: Text('i am the first'),
);
}
}
class SecondWidget extends StatelessWidget {
#override
Widget build(BuildContext context){
return Container(
child: Text('i am the second'),
);
}
}
createWidget is the important:
class Main extends StatelessWidget {
// maybe return a widget i wanna, maybe return a default widget.
Widget _createWidget(widget){
// do something to judge
if(dosomething){
return Container(
child: Text('nothing'),
);
}
// i wanna `widget()` at this postion. not when `_createWidget`
return widget();
}
#override
Widget build(BuildContext context){
return Column(
children: <Widget>[
_createWidget(FirstWidget),
_createWidget(SecondWidget),
],
);
}
}
You can pass an instance of the Widget to your function and then return it:
#override
Widget build(BuildContext context){
return Column(
children: <Widget>[
_createWidget(FirstWidget()),
_createWidget(SecondWidget()),
],
);
}
Widget _createWidget(Widget widget) {
// ... other stuff...
return widget;
}
Or if you want to defer constructing FirstWidget() and SecondWidget() until after you've called _createWidget() (for example, if you want _createWidget to return the constructed widget conditionally), you could use an anonymous function to create a thunk:
#override
Widget build(BuildContext context){
return Column(
children: <Widget>[
_createWidget(() => FirstWidget()),
_createWidget(() => SecondWidget()),
],
);
}
Widget _createWidget(Widget Function() widgetBuilder) {
// ... other stuff...
return widgetBuilder();
}
You can pass anything to a function.
Change your function definition as follow:
Widget _createWidget(Widget child){
// do something to judge
if(dosomething){
return Container(
child: Text('nothing'),
);
}
// Notice that you just return the variable and not call it as a function.
// return child(); <-- this one will result in an error
return child; // <-- this is the right way
}