'setState' not working in navigated page if custom color is used - flutter

For some reason, whenever I navigate to another route using the way described in flutter's documentation i.e https://flutter.dev/docs/cookbook/navigation/navigation-basics, and if I have used custom color in the following way:
color: Color(0xff0e0f26),
in that route, the setState method doesn't work in it. However, if I use color in the following way: color: Colors.blue, the setState method works. I have no idea what is causing this. I want to use a color value that is not present amongst the colors that flutter provides. How do I fix this? The full code along with explanation (using comments) is here:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: homepage(),
);
}
}
class homepage extends StatefulWidget{
#override
_homepageState createState() => _homepageState();
}
class _homepageState extends State<homepage>{
#override
Widget build(BuildContext context){
return Scaffold(
body: Container(
alignment: Alignment.center,
color: Color(0xff0e0f26),
child: RaisedButton(
onPressed: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
)
);
}
}
class SecondRoute extends StatefulWidget{
#override
_SecondRouteState createState() => _SecondRouteState();
}
class _SecondRouteState extends State<SecondRoute>{
bool test = false;
#override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text("Second"),
),
body: Container(
alignment: Alignment.center,
color: Color(0xff0e0f26), //Here, if I use 'color: Colors.blue', setState works.
child:Column(
children: [
RaisedButton(
onPressed: (){ //When the button is pressed, setState is triggered.
setState((){ //This should theoretically rebuild the widget with 'test' becoming true
//, thus showing the text widget below in the screen, but it doesn't.
test = true;
});
},
),
test ? Text("HELLO"):SizedBox(), //I want 'test' to become true, thus making the text
//widget come on screen.
],
),
),
);
}
}
Thank you.

Your text ist just too dark.
If you use
test ? Text("HELLO", style: TextStyle(color: Colors.white30),):SizedBox(),
it should work. This just makes your text lighter. You should use ElevatedButton instead of RaisedButton, since RaisedButton is deprecated and could cause problems as well.

Related

Click button in child widget not detected in parent function

I am trying to understand how state works with Flutter, I have a child widget that needs to change something in the Parent widget. I can detect the push on the button in the child using a print. However when i try to bubble up this change to the parent its not catching it.
I have been though a number of tutorials and im not 100% sure I understand how state works or should work, I think I am missing something here.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
//Consider this function as your _onNotification and important to note I am using setState() within :)
void _incrementCounter() {
print("pressed"); // Does not detect pressed.
setState(() {
_counter++; // counter is not increased
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
style: TextStyle(color: Colors.white),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button $_counter times:',
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
],
),
),
floatingActionButton: MyFloatButton(
_incrementCounter), //Pass delegate to _incrementCounter method
);
}
}
class MyFloatButton extends StatefulWidget {
// Here I am receiving the function in constructor as params
const MyFloatButton(this.onPressedFunction, {super.key});
// Should be the delegated function from _MyHomePageState
final Function onPressedFunction;
#override
_MyFloatButtonState createState() => new _MyFloatButtonState();
}
class _MyFloatButtonState extends State<MyFloatButton> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
color: Colors.orangeAccent,
borderRadius: new BorderRadius.circular(50.0)),
child: IconButton(
icon: Icon(Icons.add),
color: Colors.white,
onPressed: () => {
//print('pressed') // Does work shows button pressed
widget.onPressedFunction
}, // call the delegated method to detect the pressed
),
);
}
}
Things tried:
onPressed: widget.onPressedFunction()
Results in
setState() or markNeedsBuild called during build
onPressed: widget.onPressedFunction
Results in
The argument type 'Function' can't be assigned to the parameter type 'void Function()?'.
onPressed: () => widget.onPressedFunction,
Results in
No change does not do anything
Please try this. onPressedFunction is a function so declare like below function.
onPressed: () => {
widget.onPressedFunction()
},
Inside the MyFloatButton you're calling a function inside a function
onPressed: () => {
//print('pressed') // Does work shows button pressed
widget.onPressedFunction
},
It should be as follows
onPressed: widget.onPressedFunction
Change this
onPressed: () => {
//print('pressed') // Does work shows button pressed
widget.onPressedFunction
},
To this
onPressed: () => widget.onPressedFunction,
Just remove curly braces and add () here
floatingActionButton: MyFloatButton(
_incrementCounter())

flutter 2.0: nested MaterialApps -> problem with textInput inside showModalBottomSheet

In my App, I use 2 Material Apps to handle the Navigation with the BottomNavigation Bar.
As my App is pretty complex, this was the best way to do this.
On one Screen, when the user is not logged in, a bottomSheet opens, where the user can put in his login credentials.
The bottomSheet shall appear above the Navigation Bar.
In the following Code Snippet, this is how i solved it.
import 'package:flutter/material.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(),
);
}
}
final _navigationKey = GlobalKey<NavigatorState>();
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
resizeToAvoidBottomInset: true,
body: MaterialApp(
navigatorKey: _navigationKey,
routes: {
'0': (context) => Home(),
'1': (context) => Scaffold(backgroundColor: Colors.yellow),
},
initialRoute: '0',
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.account_box_rounded),
label: 'account',
),
BottomNavigationBarItem(
icon: Icon(Icons.baby_changing_station),
label: 'stuff',
)
],
onTap: (int index) {
setState(() {
currentIndex = index;
});
Navigator.of(context).popUntil((route) => !(route is PopupRoute));
_navigationKey.currentState.pushReplacementNamed(index.toString());
},
),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showModalBottomSheet(
context: context,
useRootNavigator: false,
isScrollControlled: true,
builder: (_) => Container(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: TextField(),
),
),
);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Colors.red,
);
}
}
Now to the problem:
In flutter 1.22:
When the user taps on the TextInput, the Keyboard opens and the bottomSheet moves its position above the keyboard.
In flutter 2.0:
When the user taps on the TextInput, the Keyboard opens and the bottomSheet moves its position out of screen
Does anyone have a good workaround?
what I tried so far
I gave the bottomSheet a bottom Padding:
bottom: MediaQuery.of(context).viewInsets.bottom),
But it didn't solve the problem
I think the app structure is a bit strange with the two nested MaterialApps.
The behavior you described is not exactly the same I get when I use your code on my device.
However in my case I solved the problem by setting resizeToAvoidBottomInset: false to both the Scaffold elements.
I had the same issue, I fixed using a SingleChildScrollView inside my bottom sheet.
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: ...,
),
);
});
Source: https://github.com/flutter/flutter/issues/18564#issuecomment-586205403

Flutter, open the Text displayed inside a Card in an Alert dialog

in flutter, i'm trying to get the text in the card to be displayed in the Alert Dialog when the card is tapped just like in the pictures below:
image 1
image 2
I merely ask to be pointed in the right direction of how something like this would be acomplished. Although hand-helding would be appreciated, i ask atleast for where to start at.
Thank you in advance!
What I would suggest is having the String for the text of the card stored inside a variable, what you would then do is onTap of the card you call a function that you create and pass the variable that you stored the string of the text is showMyDialog(textVariable) which would call the function to display the dialog and then display the text using the textVariable.
I've attached a more accurate example to achieve what you want.
You can tweak the UI as u wish
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String displayText = "Same Text Everywhere";
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTap: ()=> showMyDialog(context,displayText),
child: Card(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(displayText),
),
),
)
),
);
}
Future<void> showMyDialog(BuildContext context, String displayText) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(displayText),
);
},
);
}
}

Navigating to another page from drawer menu and setting title to app bar

i am new to flutter and would like someone to help me with code i found in github that i would like to use. take a look at the link below
https://github.com/JohannesMilke/drawer_example
this is an example of a navigational drawer. i like the way the developer coded it and would like to use this example. the problem is that the developer didnt implement navigating to another page. when you click on item in the drawer, it just print a message in the console.
i want to take this a step further. i want to modified the code so that when you click on a item it will navigate to another page and the drawer will b closed. the drawer icon should remain on the toolbar on the new page displayed. also, when you navigate to another page the title of that page should be set in the toolbar.
when i looked at the code , i have an idea where to change but i am not successful. i think i need to change the body tag at the bottom of the code. the problem is that i dont know how to call the DrawerWidgetState class in drawer_widget.dart file.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final String appTitle = 'Ttitle';
#override
Widget build(BuildContext context) => MaterialApp(
title: appTitle,
theme: ThemeData(
primaryColor: Colors.red,
textTheme: TextTheme(
subhead: TextStyle(
color: Colors.black.withOpacity(0.4),
),
),
dividerColor: Colors.black.withOpacity(0.4),
),
home: MainPage(appTitle: appTitle),
);
}
class MainPage extends StatefulWidget {
final String appTitle;
const MainPage({this.appTitle});
#override
MainPageState createState() => MainPageState();
}
class MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(widget.appTitle),
),
drawer: DrawerWidget(),
body: container()
);
}
i define the following function in drawer_widget.dart file
getDrawerItemWidget(int pos) {
print('testing');
switch (pos) {
case 0:
return new FirstFragment();
case 1:
return new SecondFragment();
case 2:
return new ThirdFragment();
default:
return new Text("Error");
}
}
but i dont know how to call it from Mainpage Body tag and set title accordingly. can someone help modify the code so that i can nagivate to another page and set title? full code is in
https://github.com/JohannesMilke/drawer_example
thanks in advance
Using the drawer_example library you need to make some small changes in order to make it work.
Over your drawer_widget.dart add this add the beginning:
typedef TitleCallback = void Function(String, int);
Once you do that, your Drawer StatefulWidget should looks this way:
class DrawerWidget extends StatefulWidget {
final TitleCallback callback;
final int tabIndex;
#override
DrawerWidgetState createState() => DrawerWidgetState();
DrawerWidget(this.callback, this.tabIndex);
}
and your initState:
#override
void initState() {
selectedDrawerIndex = widget.tabIndex;
selectedProfileIndex = 0;
super.initState();
}
This will be the constructor to pass the new value back to your main.dart file.
Inside the ListTile, you can add the following logic:
ListTile(
leading: Icon(item.icon),
title: Text(item.name),
selected: selectedDrawerIndex == currentIndex,
onTap: () {
final item = getOffsetIndex(drawerGroups, currentIndex);
print('Selected index $selectedDrawerIndex with name ${item.name}');
setState(() {
selectedDrawerIndex = currentIndex;
widget.callback(item.name, selectedDrawerIndex);
});
Navigator.pop(context); // to close the Drawer
},
)
If you can check, the line: widget.callback(item.name); sends the tab name over the callback and that logic can be applied any where you want to change your title. It can even be a hard coded title like:
widget.callback("Second Tab");
Now, going back to your main.dart file:
class MyApp extends StatefulWidget {
final String title;
ListExample(this.title);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Widget> _fragments = <Widget> [
Container(
child: Text("Fragment One"),
),
Container(
child: Text("Fragment Two"),
),
Container(
child: Text("Fragment Three"),
),
];
String titleAppBar = "Testing";
int tabIndex = 0;
#override
void initState() {
setState(() {
titleAppBar = widget.title;
});
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: widget.title,
home: Scaffold(
appBar: AppBar(
title: Text(titleAppBar),
),
drawer: DrawerWidget((title, index) {
setState(() {
titleAppBar = title;
tabIndex = index;
});
}, tabIndex),
body: _fragments[tabIndex],
),
);
}
}
Final Result:
Looking at the example on GitHub, it's overcomplicating something that's too easy with Flutter.
Here's a simple example on how to use a Drawer on Flutter:
main.dart
import 'package:flutter/material.dart';
import 'another_page.dart';
import '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,
),
// declaring your routes will allow you to push and remove everything from the stack (including the drawer) with pushNamedAndRemoveUntil()
routes: {
'home': (context) => HomePage(),
'anotherPage': (context) => AnotherPage(),
},
initialRoute: 'home',
);
}
}
home_page.dart (another_page.dart is exactly the same for illustration purpose)
import 'package:flutter/material.dart';
import 'menu_drawer.dart';
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: MenuDrawer(),
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Text('Home'),
),
);
}
}
menu_drawer.dart
import 'package:flutter/material.dart';
class MenuDrawer extends StatelessWidget {
// Push the page and remove everything else
navigateToPage(BuildContext context, String page) {
Navigator.of(context).pushNamedAndRemoveUntil(page, (Route<dynamic> route) => false);
}
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
// This could be mapped from a List of items
children: <Widget>[
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
onTap: () => navigateToPage(context, 'home'),
),
ListTile(
leading: Icon(Icons.panorama),
title: Text('Another page'),
onTap: () => navigateToPage(context, 'anotherPage'),
),
],
),
);
}
}
Final result:

Flutter Web persistent header

With the introduction of flutter for web it has me trying to achieve a website style header that is persistent when using routes and across the entire app. Appbar doesn't appear to be the solution since each scaffold has its own appBar. I've created the header widget that's in a Column with the MaterialApp. However, this implementation feels wrong as everything should be a child of MaterialApp or CupertinoApp.
If the searchBar header can be placed within the MaterialApp and I'm able to use Navigator that's would be preferred. I'm really here for guidance and the "right" way to do this.
void main() {
initKiwi();
// BlocSupervisor().delegate = AppBlocDelegate();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Material(
elevation: 2.0,
color: Colors.white,
child: MediaQuery(
data: MediaQueryData.fromWindow(ui.window),
child: Directionality(
textDirection: TextDirection.ltr,
child: Container(
height: 50,
child: SearchBar(),
),
),
),
),
Expanded(
child: MaterialApp(
title: 'Discover Brindle',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Brdl',
),
home: Text("Pages & Routes Here"),
),
),
]);
}
}
Though it's not using routes I was able to solve this using IndexedStack. This also preserves any scrolling I've done in the ProductsPage() when closing the search page. The AppBar is persistent and was able to keep the code to a minimum.
main.dart
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Discover Brindle',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Brdl'
),
home: MainPage(),
);
}
}
main_page.dart
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final _searchBloc = kiwi.Container().resolve<SearchBloc>();
final _productsBloc = kiwi.Container().resolve<ProductsBloc>();
PageController pageController;
int currentPage = 0;
void _onSearchActive({bool isActive}) {
setState(() {
this.currentPage = isActive ? 1 : 0;
});
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: _buildScaffold(),
);
}
Widget _buildScaffold() {
return BlocProviderTree(
blocProviders: [
BlocProvider<SearchBloc>(bloc: _searchBloc),
BlocProvider<ProductsBloc>(bloc: _productsBloc),
],
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: SearchBar(onIsActive: _onSearchActive),
),
body: IndexedStack(
children: [
ProductsPage(),
SearchPage(),
],
index: currentPage,
),
),
);
}
}