I am creating an app using Flutter.
On iOS however (you can also test it on Android), dark theme is not applied.
Using Android widgets, it is working fine tho.
How can I make the Cupertino widgets using the dark theme? Especially for the popups.
I am using Flutter 1.9.1+hotfix6
E.g. the Cupertino "ActionSheet":
import 'package:flutter/material.dart';
import 'home.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
darkTheme: ThemeData.dark(),
home: Home(),
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('test'),
onPressed: () {
Widget secondaryButton, confirmButton, popup;
secondaryButton = CupertinoActionSheetAction(
child: Text('secundary'),
onPressed: () {},
);
confirmButton = CupertinoActionSheetAction(
child: Text('test'),
onPressed: () {},
);
popup = CupertinoActionSheet(
title: Text('Title'),
message: Text('Content'),
cancelButton: secondaryButton,
actions: [confirmButton],
);
showCupertinoModalPopup(
context: context, builder: (context) => popup);
},
),
);
}
}
Screenshot:
Check this repo, you can create platform specific layouts only using a single widget that does all the platform specific boilerplate for you.
Also have support for dark mode, at least in iOS.
I have pushed to two screen and wish to go back to my main home page. I tried doing that by using popUntil however it is not giving me the req result and just showing a black screen. Do i need to set a new route to my main page , even though i don't want to create a new instance of it ?
My code:
class Completed extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Completed Screen',
home: CompleteScreen(),
routes: <String, WidgetBuilder>{
// "/my-app": (BuildContext context) => MyApp()
}
);
}
}
class CompleteScreen extends StatelessWidget{
#override
Widget build(BuildContext context){
Container Complete = Container(
child: Column(
.....
FlatButton(
onPressed: (){
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
// Navigator.popUntil(context, ModalRoute.withName(Navigator.defaultRouteName));
},
),
],
));
return Scaffold(
body: Complete
);
}
}
My main page
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: DefaultTabController(length: 2,child: MyHomePage(title: '')),
routes: <String, WidgetBuilder>{
"/TaskScreen": (BuildContext context) => new task(),
}
);
}
}
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) {
return Ink(
child: InkWell(
onTap: (){
Navigator.of(context).pushNamed("/TaskScreen");
},
child: Card(
...
),),); },);
return Scaffold(
...
}
}
I tried using '/TaskScreen' and '/my-app' however even that didn't work.
You could try this
Navigator.popUntil(
context,
ModalRoute.withName(
Navigator.defaultRouteName,
),
),
As defaultRouteName works as the first screen opened when the app starts.
EDIT
So, as mentioned below, named routes won't work with Navigator.defaultRouteNamenor route.isFirst, the best approach to solve this I've found is declaring all your routes in the main page, as these will become global (or that's what I understood), so your code would end something like this
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: DefaultTabController(length: 2,child: MyHomePage(title: '')),
routes: <String, WidgetBuilder>{
"/": (BuildContext context) => MyApp(), (or MyHomePage())
"/TaskScreen": (BuildContext context) => new task(),
}
);
}
}
With that done, anytime you want to go back to the main page you just have to call
Navigator.popUntil(context, ModalRoute.withName('/'));
Hope that works for you.
The route in the popUntil has a property called isFirst that returns true if the route is the first route in the navigator. So in your case, you can use something like:
Navigator.of(context).popUntil((route) {
return route.isFirst;
});
I had pushed a new Screen from my main screen and it had navigated smoothly and also showed the back button however now that I run the button, the default button is not showing in my code.
The only change that I made was that I changed the route in my main file from taskScreen() to task().
But this was essential as i wanted to make some more routes from my second screen and so it had to return a Material App.
How can i get the default back button back?
My code:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: DefaultTabController(length: 2,child: MyHomePage(title: '')),
routes: <String, WidgetBuilder>{
"/TaskScreen": (BuildContext context) => new task(), //CHANGED HERE
},
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
..
),
body: ...
}
}
My second screen
class task extends StatelessWidget{
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Task',
home: new taskScreen(),
routes: <String, WidgetBuilder>{
"/Completed": (BuildContext context) => new Completed()
}
);
}
}
class taskScreen extends StatefulWidget{
#override
taskState createState() => new taskState();
}
class taskState extends State<taskScreen> {
#override
Widget build(BuildContext context) {
Column taskScreen = Column (...)
return Scaffold(
appBar: AppBar(
title: Text('Task Screen')),
body: taskScreen,
);
}
}
Class name must to start with uppercase character. You have wrong structure, MaterialApp must to be only one time.
https://dart.dev/guides/language/language-tour
For me it happened when I added a draw to all my various AppBars ... quick googling provided a solution which worked for me:
I added to the AppBar the following which let flutter take a conscious decision whether there is or is not what to go back to ...
automaticallyImplyLeading: true,
Then I added defined how the "leading" should look like (also to the appbar):
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context, false),
),
Making a long story short: Adding the following to the respective AppBar did the work:
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context, false),
),
Remove the second routes MaterialApp and replace it with an AppBar
How do you navigate to a new screen in Flutter?
These questions are similar, but are asking more than I am.
Flutter - Navigate to a new screen, and clear all the previous screens
Flutter: How do I navigate to a new screen using DropDownMenuItems
Flutter: Move to a new screen without back
flutter navigation to new screen not working
I am adding an answer below.
Navigate to a new screen:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
where context is the BuildContext of a widget and NewScreen is the name of the second widget layout.
Code
main.dart
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),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Center(
child: ElevatedButton(
child: const Text(
'Navigate to a new screen >>',
style: TextStyle(fontSize: 24.0),
),
onPressed: () {
_navigateToNextScreen(context);
},
),
),
);
}
void _navigateToNextScreen(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
}
}
class NewScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('New Screen')),
body: const Center(
child: Text(
'This is a new screen',
style: TextStyle(fontSize: 24.0),
),
),
);
}
}
See also
Documentation
Navigator and Routes and Transitions... Oh, My! - Simon Lightfoot | Flutter Europe
To load new screens with Flutter pre-canned animations, use their respective transition classes. For example:
Container Transformation
Basically we have the first widget or screen transform into the next screen. For this we need to use OpenContainer. The following code illustrates an item in a ListView transformed to its details page.
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
elevation: 2.0,
child: OpenContainer(
transitionType: ContainerTransitionType.fadeThrough,
closedColor: Theme.of(context).cardColor,
closedElevation: 0.0,
openElevation: 4.0,
transitionDuration: Duration(milliseconds: 1500),
openBuilder: (BuildContext context, VoidCallback _) => THENEXTSCREEN(),
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return ListTile(
leading: Icon(Icons.album),
title: Text("ITEM NAME"),
);
},
),
);
}
Shared Axis
This transition is similar to that in Tab or Stepper. We need SharedAxisTransition, PageTransitionSwitcher, along with a state to model transition between active and previous page. If we only switch between two pages we can use a simple boolean isFirstPage for it. Here's the snippet with Provider as state management:
#override
Widget build(BuildContext context) {
return Consumer<YourState>(
builder: (context, state, child) {
return PageTransitionSwitcher(
duration: const Duration(milliseconds: 1500),
reverse: !state.isFirstPage, // STATE
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
child: child,
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal,
);
},
child: state.isFirstPage? FIRSTPAGE() : SECONDPAGE(), // STATE
);
},
);
}
Note that in all these scenarios we don't use Navigator and MaterialPageRoute. All these codes are derived from animations repo so you may want to check it out first.
Navigate to next screen with back using Navigator.push()
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),);
Navigate to next screen without back using Navigator.pushReplacement()
Navigator.pushReplacement(
context,MaterialPageRoute(builder: (context) => SecondRoute()),);
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => NextScreenName()));
}
If you are familiar with web development this approach is similar to routing.
main.dart
void main() {
setupLocator();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
routes: {
'/' : (BuildContext context)=>HomePage(),
'/register' : (BuildContext context)=>RegisterPage(),
},
);
}
}
You can add button onPressed event from the homepage.dart to navigate register.dart as follows.
onPressed: (){
Navigator.pushReplacementNamed(context, '/register');
},
Here is a full example of routes push / pop:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Routes',
routes: {
'/login': (BuildContext context) => Login(),
// add another route here
// '/register': (BuildContext context) => Register(),
},
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Routes'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
onPressed: () {
// This gives the back button:
Navigator.of(context).pushNamed('/login');
// This doesn't give the back button (it replaces)
//Navigator.pushReplacementNamed(context, '/login');
},
child: Text('Login'),
),
],
),
),
);
}
}
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login Page'),
),
body: Center(
child: RaisedButton(
onPressed: () {
// This will only work for pushNamed
Navigator.of(context).pop();
},
child: Text('Go back'),
),
));
}
}
you can use that way in your build widget
onTap: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => NewScreen()));},
In formal method :
Navigator.push(context, MaterialPageRoute(builder: (context)=>Second()));
In GetX method :
Get.to(Second());
If we can navigate screen into another page and delete current page from stack then we can use method which is define below :
Get.off(Third());
If we can navigate screen into another page and delete all route or page from stack then we can use the method which is define below :
Get.offAll(Third());
If we want to use Navigator.pop() then GetX give a Method which is define below :
Get.back();
You can try with the following code
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => YourNextScreen())),
I found a good tutorial that I have followed along, it is very comprehensive with screenshots and step by step, you can also download the code and just run it. Very helpful for me learning Flutter especially I am totally a begineer.
https://medium.com/#misterflutter/lesson-5-creating-new-screens-f740994190c7
https://medium.com/#misterflutter/lesson-6-creating-new-screens-part-2-4997085a43af?sk=d2a0fb723af42b78800f7cf19b312b62
With the Get plugin, you can navigate to a new page by simply calling
Get.to(Page());
This way you can present the next screen
Navigator.of(context).push(
MaterialPageRoute(fullscreenDialog: true,
builder: (context) => const NewScreen(),
),
);
FloatingActionButton(
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const AddUser()));
},
child: const Icon(Icons.add),
),
By default, the endDrawer icon in flutter is the hamburger icon. I wanna change it to a filter icon.
new Scaffold(
endDrawer: Drawer(),
...
}
This should do what you want:
import 'package:flutter/material.dart';
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
endDrawer: Drawer(),
appBar: AppBar(
actions: [
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.filter),
onPressed: () => Scaffold.of(context).openEndDrawer(),
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
),
),
],
),
body: new Container(),
),
);
}
}
void main() => runApp(App());
Note the 'Builder' is necessary so that the IconButton gets the context underneath the Scaffold. Without that, it would instead be using the context of the App and therefore wouldn't be able to find the Scaffold.
A different (cleaner?) option would be to make a StatelessWidget that encloses IconButton.