Flutter - losing state when pressing back button to android home screen - android

I have an issue with flutter. I have managed to implement a basic navigation system that keeps state when you do either of the following:
switch between tabs
press the android home button and re-open the app (either by clicking on the app again or using the list of active app button (the little square at the bottom))
But if I press the back button - going back to the android homescreen I completely lose state. I have re-implemented some code to randomly generate a number and display it on the app - this way I know if I'm getting the same widget or a new one has been built.
Why do I need this? (if you're interested)
I'm creating an audio app and when I click play song, it plays. but when I click back to the home screen and let it play in the background -> then open the app again, I can play it again and have it playing twice!
Main:
import 'package:flutter/material.dart';
import 'BottomNavigationBarController.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Login',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BottomNavigationBarController(),
);
}
}
Bottom navigation tab (BottomNavigationBarController):
import 'package:flutter/material.dart';
import 'PlaceholderWidget.dart';
class BottomNavigationBarController extends StatefulWidget {
BottomNavigationBarController({Key key}) : super(key: key);
#override
_BottomNavigationBarController createState() => _BottomNavigationBarController();
}
class _BottomNavigationBarController extends State<BottomNavigationBarController>{
int _selectedPage = 0;
List<Widget> pageList = List<Widget>();
#override
void initState() {
pageList.add(PlaceholderWidget());
pageList.add(PlaceholderWidget());
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _selectedPage,
children: pageList,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.phone_android),
title: Text('First Page'),
),
BottomNavigationBarItem(
icon: Icon(Icons.phone_android),
title: Text('Second Page'),
),
],
currentIndex: _selectedPage,
selectedItemColor: Colors.blue,
onTap: _onItemTapped,
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
void _onItemTapped(int index) {
setState(() {
_selectedPage = index;
});
}
}
Random number widget (PlaceholderWidget):
import 'dart:math';
import 'package:flutter/material.dart';
class PlaceholderWidget extends StatefulWidget {
PlaceholderWidget({Key key, this.color}) : super(key: key);
final Color color;
#override
_PlaceholderWidget createState() => _PlaceholderWidget();
}
class _PlaceholderWidget extends State<PlaceholderWidget> with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
return Container(
color: widget.color,
child: Text(random_num().toString()),
);
}
int random_num(){
Random random = new Random();
int randomNumber = random.nextInt(100);
return randomNumber;
}
}
Any help will be appreciated :)

I think Navigator pop deletes the widget not entirely sure. But If you want to save the current state just use navigator push don't pop. Also use named routes this will help you greatly.

Use Provider to pass state and keep a global store.
If your app will need to scale, now is a good time to start with MobX/BLoC/Redux/InheritedWidget.. etc.

Related

Keyboard immediately disappear when onTap event fired in Flutter

I'm developing a flutter app using Bloc, Division, and Get for the navigation.
I have a text field reusable component that I use in most of the fields that I create, the text field works properly on iOS without any issues, but on Android the Keyboard immediately disappear when I tap the field.
Here are the things that I've done but it still doesn't work:
Not using text field from the component, but directly create another one on the page
Create my own testing page that only used to test the text field
Using resizeToAvoidBottomInset: false, defined the controller as a static final
The only thing that works is when I create another flutter project from the start.
my-testing.dart
import 'package:flutter/material.dart';
class MyTesting extends StatefulWidget {
const MyTesting({Key? key}) : super(key: key);
#override
State<MyTesting> createState() => _MyTestingState();
}
class _MyTestingState extends State<MyTesting> {
late TextEditingController controller;
#override
void initState() {
controller = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Just testing'),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: TextField(
controller: controller,
),
),
);
}
}

how to get notified whenever the user changes the os theme in flutter?

I am trying to get notified, whenever the user changes the theme of the operating system. I want to use Provider to accomplish that, however dart Provider needs a Stream that gives a Snapshot, whenerver somthing is changed or getsupdated. So I need to emplement or rather use a Stream, that gives me a snapshot whenever the os theme gets changed.
Here is my code. It is nothing special. But I really want to know how to get this Provider up and running with a Stream
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MaterialApp(initialRoute: '/', routes: {
'/': (context) => MainPage(),
}));
This class is a wrapper for the HomePage. It contains the Provider.
(value: brightnessStream) is a dummy value, and that is what I need to implement.
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return StreamProvider<Brightness>.value(
initialData: Brightness.light,
value: brightnessStream,
child: Home(),
);
}
}
In this class I am listening to the Stream, whenever the brightness changes and displying a text that shows the current theme.
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
final brightness = Provider.of<Brightness>(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('App'),
),
body: Center(
child: Text(brightness.toString()),
),
);
}
}
the stream should like somethig like this.
Stream<Brightness> get brightnessStream {
// return stream of os brigtness (os theme)
}
So how is it possible?
Here's how you can set different colors for light and dark mode, the app will automatically switch if the phone is set to dark mode or light mode.
MaterialApp(
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.red,
),
darkTheme: ThemeData(
brightness: Brightness.dark,
// additional settings go here
),
);
You can also get the platform brightness (Brightness.light / Brightness.dark) using
WidgetsBinding.instance.window.platformBrightness
but you will have to use the WidgetsBindingObserver mixin and override the method below
#override
void didChangePlatformBrightness() {
print(WidgetsBinding.instance.window.platformBrightness); // should print Brightness.light / Brightness.dark when you switch
super.didChangePlatformBrightness(); // make sure you call this
}
and then inside the didChangePlatformBrightness you can add to your stream.
This is also duplicate.
click here to view
Thank you for your answers. I solved the Problem like this:
class Theme {
final window = WidgetsBinding.instance.window;
final _controller = StreamController<Brightness>();
Theme() {
window.onPlatformBrightnessChanged = () {
// This callback gets invoked every time brightness changes
final brightness = window.platformBrightness;
_controller.sink.add(brightness);
};
}
Stream<Brightness> get stream => _controller.stream;
}
so I built my own stream

connect Flutter code from different sources like for example youtube tutorials

Hello Guys im new to flutter.
To understand Flutter I watched a lot of videos and read blog entries.
But there is always a problem:
Each video is about a specific topic and all of them start with a new Flutter project. As long as I want to continue working on the code I can't change the code.
Below I have added a code by Hanz Müller as an example. Topic NavigationBar.
But now I want to delete the text under the icons and edit the different app pages (body) with text and images.
I can't delete the text under the icons because text can't be ''null''.
And I can't edit the diffrent body pages because I can't find the position.
i only know html and css because it is a hobby and now i search for the place where i find the body container :)
Thanks a lot for your help
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class Destination {
const Destination(this.title, this.icon, this.color);
final String title;
final IconData icon;
final MaterialColor color;
}
const List<Destination> allDestinations = <Destination>[
Destination('Home', Icons.home, Colors.teal),
Destination('Business', Icons.business, Colors.cyan),
Destination('School', Icons.school, Colors.orange),
Destination('Flight', Icons.flight, Colors.blue)
];
class DestinationView extends StatefulWidget {
const DestinationView({ Key key, this.destination }) : super(key: key);
final Destination destination;
#override
_DestinationViewState createState() => _DestinationViewState();
}
class _DestinationViewState extends State<DestinationView> {
TextEditingController _textController;
#override
void initState() {
super.initState();
_textController = TextEditingController(
text: 'sample text: ${widget.destination.title}',
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${widget.destination.title} Text'),
backgroundColor: widget.destination.color,
),
backgroundColor: widget.destination.color[100],
body: Container(
padding: const EdgeInsets.all(32.0),
alignment: Alignment.center,
child: TextField(controller: _textController),
),
);
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin<HomePage> {
int _currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
top: false,
child: IndexedStack(
index: _currentIndex,
children: allDestinations.map<Widget>((Destination destination) {
return DestinationView(destination: destination);
}).toList(),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (int index) {
setState(() {
_currentIndex = index;
});
},
items: allDestinations.map((Destination destination) {
return BottomNavigationBarItem(
icon: Icon(destination.icon),
backgroundColor: destination.color,
title: Text(destination.title)
);
}).toList(),
),
);
}
}
void main() {
runApp(MaterialApp(home: HomePage(), debugShowCheckedModeBanner: false));
}
If you want to remove the Text under the icon Check the code where the Text widget is place.
So you have the relevant Text widget in BottomNavigationBarItem
title: Text(destination.title)
So if you don't need the Text widget you can simply replace it with Container to display nothing.
title: Text(destination.title)
I would suggest you read the code and understand it will. The better you understand how your widgets are built and rendered it will be easier to modify them.

How to create triple click button in flutter

How can I implement a triple-click button in flutter?
On triple-click, the button will store an entry in Firebase database.
Its pretty easy. You can use a GestureDetector() to check for the number of taps then you can provide your logic if there are 3 taps.
GestureDetector(
onTap: () {
int now = DateTime.now().millisecondsSinceEpoch;
if (now - lastTap < 1000) {
print("Consecutive tap");
consecutiveTaps ++;
print("taps = " + consecutiveTaps.toString());
if (consecutiveTaps == 3){
// Do something
}
} else {
consecutiveTaps = 0;
}
lastTap = now;
},
child: ...
)
Implementing "triple click" button in flutter might not be possible. But, if you really want to make it work ASAP then a simple method could be to maintain a counter for the number of clicks done. Once the count reaches 3, you need to add your entry to Firestore.
I have modified the code from the counter app boilerplate of flutter.
Hope you have cloud_firestore in your pubspec.yaml file. If not then add it and put the services.json as well in the app folder of android or respective directory of ios.
cloud_firestore: ^0.13.4+1
So, now you can have a look at the code that I am using.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
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> {
int _counter = 0;
_incrementCounter() {
setState(() {
_counter++;
});
if (_counter == 3) {
Firestore.instance
.collection('/sampleData')
.add({'data': "data"}).catchError((e) {
print(e);
});
setState(() {
_counter = 0;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I have edited the _incrementCounter function. I added a conditional statement to check the _counter if it is 3 or not. Then, I am adding the Firestore entry. Later on, the most important bit is to set the _counter as 0 so that the next time the user presses the button 3 times, then the code will work accordingly. You can customize it according to your needs.
But remember, triple clicks have not been yet invented in flutter and this is just a work-around solution and don't use it for Real-life Development applications as this would be a very bad practice.

Navigation to sub-screen from BottomNavigationBar-sceeen in Flutter

I´m currently working on my first simple flutter application and am trying to figure our the best approach to handle the navigation between screens.
Already Possible:
Navigation through screens with BottomNavigationBar + BottomNavigationBarItem
Navigation with Navigator.push(context,MaterialPageRoute(builder: (context) => Screen4()),);
Problem:
Having sub-screens in the screens of BottomNavigationBar
Code Example:
I want to have three main screens Screen1(), Screen2() and Screen3() accessible from the BottomNavigationBar. In Screen1() there is a button to navigate to another screen, let´s call it Screen4(), where the user can choose from a list of items. You can then add all chosen items to a list and navigate back to Screen1().
To achieve this I created the code below. The main Widget of the body will be changed according to the current index of the selected BottomNavigationItem.
main.dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const String _title = 'Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyApp(),
);
}
}
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _selectedIndex = 0;
static const List<Widget> _widgetOptions = <Widget>[
Screen1(),
Screen2(),
Screen3(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Stackoverflow Example'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Screen1'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Screen2'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('Screen3'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
The problem is when I navigate within the Screen1() - Widget to Screen4() by using
Navigator.push(context,MaterialPageRoute(builder: (context) => Screen4()),);
the navigation will happen outside of MyApp() and therefore there is no Scaffold.
If someone has an example, where this is achieved I´d be very happy.
Thank you for reading.
I know it's a bit late, but hopefully, it can help you.
To achieve this you can use the Offstage widget with a navigator as its child, this way you will have a persistent navigation bar throughout all of your pages.
you can follow this article for more details and how to implement it
https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf
you might need to tweak it a little to match your case.
info about the Offstage widget:
https://api.flutter.dev/flutter/widgets/Offstage-class.html
I suggest that you run a CupertinoApp instead of a MaterialApp. Use a CupertinoTabScaffold instead of Scaffold. Add a CupertinoTabBar as tabBar and return a CupertinoTabView as a tabBuilder
example of tabBuilder
tabBuilder: (context, index) {
if (index == 0) {
return CupertinoTabView(
navigatorKey: firstTabNavKey,
builder: (BuildContext context) => Screen1(),
Have a look at this great short article on how to implement a Tab Bar and also allow for Navigation between screens: link

Categories

Resources