When i invoke the loadingDelete method upon deleting a post where the Navigator.push.. takes place, i am directed to the Profile page but with my bottom navigation bar empty (appearing empty where icons(content) are blank).
I keep on encountering this problem when i either upload or delete a post..I tried replacing scaffold with MaterialApp but did't work...
This is where my loadingDelete method resides:
class PostStateless extends StatelessWidget {
final Post post2;
PostStateless(this.post2);
#override
Widget build(BuildContext context) {
print("REACHED HERE BEG "+post2.toString());
return new Scaffold(
resizeToAvoidBottomInset: false,
body:PostPage(post2),
);
}
}
class PostPage extends StatefulWidget {
final Post post2;
PostPage(this.post2);
#override
PostPageState createState() => new PostPageState(post2);
}
class PostPageState extends State<PostPage> with TickerProviderStateMixin {
...
..
loadingDelete()
{
if(!loadingDeletePost)
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("lib/assets/BackToEarth.jpg"),
fit: BoxFit.cover,
),
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min, //centered things bil nos
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.black),
)
]),
/* add child content here */
));
else {
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) =>
Profile()), (Route<dynamic> route) => false);
//Navigator.push alone redirects me to the profile page with blank nav bar plus arrow back
visible in my app bar(Worse).
}
}
....
}
This is my Profile page structure:
class Profile extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ProfilePage(),
);
}
}
class ProfilePage extends StatefulWidget {
#override
_ProfilePageState createState() => new _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
...
...
}
This is the structure of my global bottom nav bar which resides in my mainn dart file under MaterialApp Widget:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Instagram',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
ManageUserModel user;
class _MyHomePageState extends State<MyHomePage> {
ApiService apiService = ApiService();
List<Widget> pages = [
HomePage(),
SearchPage(),
Post(),
NotificationsPage(),
ProfilePage()
];
saveVariable(ManageUserModel user) async {
// save variable
SharedPreferences sharedUser = await SharedPreferences.getInstance();
String userSt=toJson(user);
print("USERST: "+userSt);
sharedUser.setString('user', userSt);
}
#override
void initState() {
apiService.getUsers("beeso").then((result) {
setState(() {
user = result;
print("USERRRR" +user.toString());
saveVariable(user);
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 5,
initialIndex: 0,
child: Scaffold(
body: TabBarView(
children: pages,
),
bottomNavigationBar:
Container(
margin: EdgeInsets.only(bottom: 20),
child: new TabBar(
tabs: [
Tab(
icon: Icon(Icons.home),
),
Tab(
icon: Icon(Icons.search),
),
Tab(
icon: Icon(Icons.add),
),
Tab(
icon: Icon(Icons.favorite),
),
Tab(
icon: Icon(Icons.perm_identity),
),
],
unselectedLabelColor: Colors.black,
labelColor: Colors.blue,
indicatorColor: Colors.transparent,
),
),
),
);
}
}
Any help is appreciated!!
Change the navigation to the following code:
Navigator.of(context).popUntil((route) => route.isFirst);
Related
I am new to Flutter and went on to do the codelabs - first flutter app
Since I'm learning Flutter to develop mobile apps, this tutorials use of NavigationRail isn't too good looking on a phone. I tried to switch it out for a BottomNavigationBar. When changing the background color of the navbar I noticed it doesnt expand to use the full screen. Is it always like this, or is there something making it display it this way in the code?Could'nt find any useful information about this case.
Is it possible to make the green background cover the, here black, area at the bottom of the screen?
Area under bar, white when debugging on real device, here it is black
The final code from the tutorial is poorly adjusted to:
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyAppState(),
child: MaterialApp(
title: 'Namer App',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
),
home: MyHomePage(),
),
);
}
}
class MyAppState extends ChangeNotifier {
var current = WordPair.random();
void getNext() {
current = WordPair.random();
notifyListeners();
}
var favorites = <WordPair>[];
void toggleFavorite() {
if (favorites.contains(current)) {
favorites.remove(current);
} else {
favorites.add(current);
}
notifyListeners();
}
}
class MyHomePage extends StatefulWidget {
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var selectedIndex = 0;
#override
Widget build(BuildContext context) {
Widget page;
switch(selectedIndex){
case 0:
page = GeneratorPage();
break;
case 1:
page = FavoritesPage();
break;
default:
throw UnimplementedError('no widget for $selectedIndex');
}
return LayoutBuilder(
builder: (context, constraints) {
return Scaffold(
body: Center(
child: page,
),
bottomNavigationBar: BottomNavigationBar (
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: 'Favorites',
),
],
currentIndex: selectedIndex,
onTap: _onItemTapped,
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
elevation: 0.0,
),
);
}
);
}
void _onItemTapped(int index){
setState(() {
selectedIndex = index;
});
}
}
class FavoritesPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
if (appState.favorites.isEmpty) {
return Center(
child: Text('No favorites yet.'),
);
}
return ListView(
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Text('You have '
'${appState.favorites.length} favorites:'),
),
for (var pair in appState.favorites)
ListTile(
leading: Icon(Icons.favorite),
title: Text(pair.asLowerCase),
),
],
);
}
}
class GeneratorPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
IconData icon;
if (appState.favorites.contains(pair)) {
icon = Icons.favorite;
} else {
icon = Icons.favorite_border;
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BigCard(pair: pair),
SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton.icon(
onPressed: () {
appState.toggleFavorite();
},
icon: Icon(icon),
label: Text('Like'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () {
appState.getNext();
},
child: Text('Next'),
),
],
),
],
),
);
}
}
class BigCard extends StatelessWidget {
const BigCard({
Key? key,
required this.pair,
}) : super(key: key);
final WordPair pair;
#override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var style = theme.textTheme.displayMedium!.copyWith(
color: theme.colorScheme.onPrimary,
);
return Card(
color: theme.colorScheme.primary,
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(20),
child: Text(pair.asLowerCase, style: style),
),
);
}
}
Tried changing elevation to 0.0, expandbody and what not. Nothing seems to be working here?
You can use SystemUiOverlayStyle class
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light
.copyWith(systemNavigationBarColor: Colors.greenAccent));
super.initState();
}
I have a Flutter StatefulWidget and in initState() method I am using WidgetsBinding.instance.addPostFrameCallback to use one instance variable (late List _tracks). like -
WidgetsBinding.instance.addPostFrameCallback((_) {
for(itr = 0; itr<_tracks.length; itr++){
// some logic
}
});
As this would get invoked after all Widgets are done. In one of the CustomPaint's painter class I am initializing that variable.
SizedBox.expand(
child: CustomPaint(
painter: TrackPainter(
trackCalculationListener: (tracks) {
_tracks = tracks;
}),
),
),
It is working fine when I have one screen, i.e the same class. But, When I am adding one screen before that and trying to navigate to this screen from the new screen it is throwing _tracks is not initialized exception.
new screen is very basic -
class MainMenu extends StatefulWidget {
const MainMenu({super.key});
#override
State<MainMenu> createState() => _MainMenuState();
}
class _MainMenuState extends State<MainMenu> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Play(),
maintainState: false));
},
child: const Text('play game'),
),
),
);
}
}
In single screen case the paint method of painter is getting called before postFrameCallback but in case of multiple it is not getting before postFrameCallback and because of that the variable is not getting initialized.
reproducible code -
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/mainMenu': (context) => const MainMenu(),
'/game': (context) => const MyHomePage(title: 'game'),
},
initialRoute: '/mainMenu',
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late List<Rect> _playerTracks;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
print(_playerTracks.length);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
color: Colors.white,
margin: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: 1,
child: SizedBox.expand(
child: CustomPaint(
painter: RectanglePainter(
trackCalculationListener: (playerTracks) =>
_playerTracks = playerTracks),
),
),
),
)
],
),
),
);
}
}
class MainMenu extends StatefulWidget {
static String route = '/mainMenu';
const MainMenu({super.key});
#override
State<MainMenu> createState() => _MainMenuState();
}
class _MainMenuState extends State<MainMenu> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 200.0,
color: Colors.white,
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/game');
},
child: const Text('play game'),
),
),
),
);
}
}
class RectanglePainter extends CustomPainter {
Function(List<Rect>) trackCalculationListener;
RectanglePainter({required this.trackCalculationListener});
#override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
const RadialGradient gradient = RadialGradient(
center: Alignment(0.7, -0.6),
radius: 0.2,
colors: <Color>[Color(0xFFFFFF00), Color(0xFF0099FF)],
stops: <double>[0.4, 1.0],
);
canvas.drawRect(
rect,
Paint()..shader = gradient.createShader(rect),
);
List<Rect> _playerTracks = [];
_playerTracks.add(rect);
trackCalculationListener(_playerTracks);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
I am very new to flutter and would highly appreciate if someone could help me figure out what I am doing wrong here.
I'm trying to set up tabs with FABs, like what's pictured in the Material Design guidelines.
I've pretty much got it working, by adding a listener on the TabController and changing my FAB there:
#override
void initState() {
...
_tabController = TabController(
length: 5,
vsync: this,
)..addListener(() {
setState(() {
_fabData = _fabDatas[_tabController.index];
});
});
...
}
#override
Widget build(BuildContext context) {
final fab = _fabData == null
? null
: FloatingActionButton(
isExtended: _fabData.expanded,
tooltip: _fabData.tooltip,
child: Icon(_fabData.icon),
onPressed: () {
_fabData.onPressed(context);
},
);
return Scaffold(
...
floatingActionButton: fab,
...
);
}
The problem is that tab controller listeners seem to be called only when the tab switch has finished completely, and not halfway through. If a user swipes from one tab to another, the tab will slide completely over, then come to a stop, and then the button will change.
Is there a way to trigger this in the middle of the swipe instead?
You do not need with listener, try this:
import 'package:flutter/material.dart';
class TabControllerApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tabs work',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _fabData = [
'Tab1',
'Tab2',
'Tab3',
'Tab4',
'Tab5'
]; // Replace with your OBJECT!!!
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _fabData.length,
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
bottom: TabBar(
tabs: _fabData
.map((String t) => Tab(
text: t,
))
.toList(),
),
),
body: TabBarView(
children: _fabData.map((String text) {
return Container(
child: Stack(
children: [
Positioned(
bottom: 16,
right: 16,
child: FloatingActionButton(
// TODO USE YOUR Object
isExtended: true, //_fabData.expanded,
//tooltip: _fabData.tooltip,
child: Icon(Icons.bookmark), //Icon(_fabData.icon),
onPressed: () {
//_fabData.onPressed(context);
},
),
)
],
),
);
}).toList()),
));
}
}
I had two Stateless Widgets in my Tab Bar View and my app was working fine. But then there was a requirement to make a button in one of the views and change the state of the button hence I made it as Stateful widgets but now the Tab Bar View doesn't accept it and gives the exception on running that ' type 'task' is not a subtype of type 'StatelessWidget''. Can we have only Stateless Widgets in TabBar views? How can i Fix this?
My main.dart file having the TabBarview
import 'package:flutter/material.dart';
import 'package:flutter_convertor/task.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DefaultTabController(length: 2,child: MyHomePage(title: '')),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context){
final list = ListView.builder(
itemBuilder: (context, position) {
//some other implementation
},
);
return Scaffold(
appBar: AppBar(
title: Text('Home'), bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
]),
),
body: TabBarView(children: [list, task()]));
}
}
My Stateful widget task
class task extends StatefulWidget{
#override
taskState createState() => new taskState();
}
class taskState extends State<task> {
int current_step = 0;
bool isButtonDisabled;
#override void initState() {
super.initState();
isButtonDisabled = false;
}
formReady(){
setState(() {
isButtonDisabled = !isButtonDisabled ;
});
}
#override
Widget build(BuildContext context) {
Column taskScreen = Column(
children: <Widget>[Expanded(
child: ListView(
children: <Widget>[
//other implementation
FlatButton(
color: Colors.red,
textColor: Colors.black,
shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(20.0)),
disabledColor: Color(0XFFf9c3c1),
disabledTextColor: Colors.white,
padding: EdgeInsets.all(8.0),
splashColor: Colors.red[400],
onPressed: isButtonDisabled ? null : _completePage
,
child: Text(
"Completed",
style: TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.bold),
),
)
,
],
)
]);
return taskScreen;
}
}
You need to use the AutomaticKeepAliveClientMixin for this to work. It's a fairly simple mixin to use. You only need to include the
#override
bool get wantKeepAlive => true;
and add super.build(context) to your build method.
Your updated class would look something like this:
class task extends StatefulWidget{
#override
taskState createState() => new taskState();
}
class taskState extends State<task> with AutomaticKeepAliveClientMixin<task>
#override
Widget build(BuildContext context) {
super.build(context);
...
}
}
I'm building an app with 3 items in the bottom navigation bar. When I change the tab, a different widget is rendering. So far, so good...
import 'package:flutter/material.dart';
class BottomTest extends StatefulWidget {
State createState() => new _BottomTestState();
}
class _BottomTestState extends State<BottomTest> {
List<Widget> _pages;
Widget _selectedContent;
int _bottomIndex;
#override
void initState() {
_bottomIndex = 0;
super.initState();
}
#override
Widget build(BuildContext context) {
_definePages();
return Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Test'),
),
body: _selectedContent ?? _pages[_bottomIndex],
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.add),
title: Text("Red")
),
BottomNavigationBarItem(
icon: Icon(Icons.location_on),
title: Text("Blue")
),
BottomNavigationBarItem(
icon: Icon(Icons.people),
title: Text("Green")
)
],
currentIndex: _bottomIndex,
onTap: _onTabTapped,
)
);
}
_definePages() {
_pages = [
Container(
color: Colors.red,
child: Stack(children: <Widget>[
_defineFloatingActionButton(),
])
),
Container(color: Colors.blue),
Container(color: Colors.green),
];
}
_defineFloatingActionButton() {
return Align(
alignment: Alignment.bottomRight,
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
//TODO: How to navigate to another page with still displaying the bottom navigation bar?
}
),
);
}
void _onTabTapped(int index) {
setState(() {
_bottomIndex = index;
_selectedContent = _pages[index];
});
}
}
//POST
class Post extends StatefulWidget {
State createState() => new _PostState();
}
class _PostState extends State<Post> {
#override
Widget build(BuildContext context) {
return Column(children: <Widget>[
PostHeader(),
Text('This is a post.')
]);
}
}
//POSTHEADER
class PostHeader extends StatefulWidget {
State createState() => new _PostHeaderState();
}
class _PostHeaderState extends State<PostHeader> {
#override
Widget build(BuildContext context) {
return ListTile(
leading: Text('Author'),
onTap: () {
//TODO: This should navigate to another page but still displaying the bottom navigation bar, too.
},
);
}
}
But I can't figure out a best practice for more advance navigation. There are 2 problems that I'm currently facing.
When tabbing the FloatingActionButton on the first page, I want to display a fourth page but the BottomNavigationBar still needs to be visible and operable.
Building a more complex app, I'm dealing with a handful of nested classes. So on my root page, there is a class "Post" and the post contains a class "PostHeader". In PostHeader, there is a ListTile with an onTap callback that should affect my _selectedContent. How do I define this callback? Passing it trough all the different classes didn't seem right.
I thought about defining it in my BottomTest.dart and passing it trough Post and PostTile but that doesn't seem like best practice to me, especially when talking about lots of required callbacks.
Thank you very, very much in advance!
I'm assuming that the fourth page will be shown as any of the other three pages and since the button is in the first page, the fourth page will take the place of the first page and still signal the first bottom "red" field as active.
If that is the case you should create an independent widget for the first page that includes all the logic you need to show other content. Thus you avoid rebuilding the main layout, including the BottomNavigationBar.
You could use something along these lines, by using a FirstPage widget:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new BottomTest(),
);
}
}
class BottomTest extends StatefulWidget {
State createState() => new _BottomTestState();
}
class _BottomTestState extends State<BottomTest> {
List<Widget> _pages;
Widget _selectedContent;
int _bottomIndex;
#override
void initState() {
_bottomIndex = 0;
super.initState();
}
#override
Widget build(BuildContext context) {
_definePages();
return Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Test'),
),
body: _selectedContent ?? _pages[_bottomIndex],
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.add), title: Text("Red")),
BottomNavigationBarItem(
icon: Icon(Icons.location_on), title: Text("Blue")),
BottomNavigationBarItem(
icon: Icon(Icons.people), title: Text("Green"))
],
currentIndex: _bottomIndex,
onTap: _onTabTapped,
));
}
_definePages() {
_pages = [
FirstPage(),
Container(color: Colors.blue),
Container(color: Colors.green),
];
}
void _onTabTapped(int index) {
setState(() {
_bottomIndex = index;
_selectedContent = _pages[index];
});
}
}
//POST
class Post extends StatefulWidget {
State createState() => new _PostState();
}
class _PostState extends State<Post> {
#override
Widget build(BuildContext context) {
return Column(children: <Widget>[PostHeader(), Text('This is a post.')]);
}
}
//POSTHEADER
class PostHeader extends StatefulWidget {
State createState() => new _PostHeaderState();
}
class _PostHeaderState extends State<PostHeader> {
#override
Widget build(BuildContext context) {
return ListTile(
leading: Text('Author'),
onTap: () {
//TODO: This should navigate to another page but still displaying the bottom navigation bar, too.
},
);
}
}
class FirstPage extends StatefulWidget {
#override
_FirstPageState createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
bool showFirst = true;
_defineFloatingActionButton() {
return Align(
alignment: Alignment.bottomRight,
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _onButtonPressed,
),
);
}
_onButtonPressed() {
setState(() {
showFirst = !showFirst;
});
}
_buildFirst() {
return Container(
color: Colors.red,
child: Stack(children: <Widget>[
_defineFloatingActionButton(),
]));
}
_buildFourth() {
return Container(
color: Colors.grey,
child: Stack(children: <Widget>[
_defineFloatingActionButton(),
]));
}
#override
Widget build(BuildContext context) {
return showFirst ? _buildFirst() : _buildFourth();
}
}
For the second point, perhaps you should open another question so you keep two, more or less, unrelated matters in different answers.