Flutter: Dark Theme on iOS - ios

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.

Related

Flutter changing theme using GetX?

I made a changeable theme in flutter and later I made multi-language support, I used getx for theme switching, I used easy_localization for language change. When I use GetMeterialapp on the main page, it doesn't work, when I do Meterialapp it works multi-language, but the theme change does not work. import 'package:get/get.dart' on another page; i am using and when i use easy_localization here easy_localization is not working. I'm canceling getx is working. I couldn't get out of this situation. If I can't solve it, I will either give up without changing the theme or multi-language support.
await EasyLocalization.ensureInitialized();
runApp(EasyLocalization(supportedLocales: [
Locale("en", "US"),
Locale("tr", "TR"),
], path: "assets/Language", saveLocale: true, child: MyApp()));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Home Page',
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
theme: Themes.light,
darkTheme: Themes.dark,
themeMode: ThemeService().theme,
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
Initialize GetStorage inside main method.
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
runApp(MyApp());
}
Create storage object.
final storage = GetStorage();
Set a default value if storage is null.
storage.writeIfNull('darkmode', false);
Read the theme mode value.
bool isDarkMode = storage.read('darkmode');
Apply the theme mode.
Switch(
value: isDarkMode ,
onChanged: (value) => storage.write('darkmode', value),
)
Full code:
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'package:get/get.dart';
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final storage = GetStorage();
#override
Widget build(BuildContext context) {
appdata.writeIfNull('darkmode', false);
return SimpleBuilder(
builder: (_)
{
bool isDarkMode = storage.read('darkmode');
return GetMaterialApp(
theme: isDarkMode ? ThemeData.dark() : ThemeData.light(),
home: Scaffold(
appBar: AppBar(title: Text("Getx Dynamic theme change"),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(isDarkMode ? 'images/night.png' :'images/day.png' ,width: 100,height: 100,),
Switch(
value: isDarkMode ,
onChanged: (value) => storage.write('darkmode', value),
)
],
),
),
),
);
},
);
}
}

One time alert dialogue in Flutter

How do I only show an alert dialogue once? I want to have an alert dialogue appear when the user first loads a screen to show an instruction. Once the dialogue is dismissed, I do not want it to appear again.
You can use Shared Preferences
When launching the app, get the value from shared pref (Ex: isFirstLoaded). If isFirstLoaded == true then show the dialog.
When the dialog is dismissed, set isFirstLoaded = false and save to shared preferences.
Below is an example (please note that the example doesn't handle the dismiss event when tapping on Back key).
Add shared_preferences to your pubspec.yaml
shared_preferences: ^0.5.1+1
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyStatelessApp());
}
class MyStatelessApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Stateless Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: StatelessWidgetDemo(),
);
}
}
class StatelessWidgetDemo extends StatelessWidget {
final keyIsFirstLoaded = 'is_first_loaded';
#override
Widget build(BuildContext context) {
Future.delayed(Duration.zero, () => showDialogIfFirstLoaded(context));
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Flutter Stateless Demo'),
),
body: Center(
child: Text('Hello'),
)));
}
showDialogIfFirstLoaded(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isFirstLoaded = prefs.getBool(keyIsFirstLoaded);
if (isFirstLoaded == null) {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Title"),
content: new Text("This is one time dialog"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Dismiss"),
onPressed: () {
// Close the dialog
Navigator.of(context).pop();
prefs.setBool(keyIsFirstLoaded, false);
},
),
],
);
},
);
}
}
}

Navigate to a new screen in Flutter

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),
),

How to change the endDrawer icon in flutter?

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.

How do I set the background color of my main screen in Flutter?

I'm learning Flutter, and I'm starting from the very basics. I'm not using MaterialApp. What's a good way to set the background color of the whole screen?
Here's what I have so far:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new Center(child: new Text("Hello, World!"));
}
}
Some of my questions are:
What's a basic way to set the background color?
What exactly am I looking at, on the screen? Which code "is" the background? Is there a thing to set the background color on? If not, what's a simple and appropriate "simple background" (in order to paint a background color).
Thanks for the help!
The code above generates a black screen with white text:
You can set background color to All Scaffolds in application at once.
Just set scaffoldBackgroundColor: in ThemeData:
MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(scaffoldBackgroundColor: const Color(0xFFEFEFEF)),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
I think you can also use a scaffold to do the white background. Here's some piece of code that may help.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Testing',
home: new Scaffold(
//Here you can set what ever background color you need.
backgroundColor: Colors.white,
),
);
}
}
Here's one way that I found to do it. I don't know if there are better ways, or what the trade-offs are.
Container "tries to be as big as possible", according to https://flutter.io/layout/. Also, Container can take a decoration, which can be a BoxDecoration, which can have a color (which, is the background color).
Here's a sample that does indeed fill the screen with red, and puts "Hello, World!" into the center:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(color: Colors.red),
child: new Center(
child: new Text("Hello, World!"),
),
);
}
}
Note, the Container is returned by the MyApp build(). The Container has a decoration and a child, which is the centered text.
See it in action here:
There are many ways of doing it, I am listing few here.
Using backgroundColor
Scaffold(
backgroundColor: Colors.black,
body: Center(...),
)
Using Container in SizedBox.expand
Scaffold(
body: SizedBox.expand(
child: Container(
color: Colors.black,
child: Center(...)
),
),
)
Using Theme
Theme(
data: Theme.of(context).copyWith(scaffoldBackgroundColor: Colors.black),
child: Scaffold(
body: Center(...),
),
)
You should return Scaffold widget and add your widget inside Scaffold
Such as this code:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(child: new Text("Hello, World!"));
);
}
}
Scaffold(
backgroundColor: Constants.defaulBackground,
body: new Container(
child: Center(yourtext)
)
)
It's another approach to change the color of background:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(backgroundColor: Colors.pink,),);
}
}
On the basic example of Flutter you can set with backgroundColor: Colors.X of Scaffold
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
backgroundColor: Colors.blue,
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
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_circle),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
I think you need to use MaterialApp widget and use theme and set primarySwatch with color that you want. look like below code,
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
home: Scaffold(
backgroundColor: Color(
0xBF453F3F),
and done :)
You can just put the six digit hexa after (0xFF**......**):
return Scaffold(
backgroundColor: const Color(0xFFE9ECEF),
.....) } )
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Your App',
theme: ThemeData(
scaffoldBackgroundColor: Colors.black,
),
home HomeScreen(),
);
}
}
Sample code:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Sample App'),
backgroundColor: Colors.amber, // changing Appbar back color
),
backgroundColor: Colors.blue, // changing body back color
),
),
);
}
As sirelon suggested, add scaffold color in the theme like this,
theme: new ThemeData(scaffoldBackgroundColor: const Color(0xFFEFEFEF)),
or can give color to individual scaffold like this
Scaffold(
backgroundColor: Color(0xFFF1F1F1),
...
);
Try the following code:
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(
theme: ThemeData(
scaffoldBackgroundColor: Colors.white, // Change the background color of all Scaffold widgets of your app here
),
home: const Scaffold(
body: Center(child: Text("Hello, World!")),
backgroundColor: Colors.white, // Change the background color of this Scaffold widget here
),
);
}
}

Resources