cupertino equivalent of `Material` - flutter

I've conducted a long research on the internet about this (what I think is mischievous) use of Material widget in flutter, Any flutter developer faced the No Material widget found. exception even when using cupertino design widgets.
A simple example to produce is
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Example extends StatelessWidget {
const Example({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Example'),
leading: IconButton(
onPressed: () {},
icon: Icon(
Icons.close,
color: Colors.black54,
),
),
),
child: Center(
child: IconButton(
onPressed: () {},
icon: Icon(
Icons.close,
color: Colors.black54,
),
),
),
);
}
}
Neither the navigationBar nor the body IconButton will work resulting in the same exception.
When facing this exception I always wrap it inside a Material to bypass it, but I think am doing it wrong.IconButton is not the only widget and I've searched a lot with no avail.
Please tell me what am I missing ? and thanks in advance.

You need to wrap your root Widget in MaterialApp() so that you can access Material components.
For example,
import 'package:flutter/material.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(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

Not sure about the Cupertino part, but (afaik) you need to wrap your app in MaterialApp() to access Material widgets and other methods etc.

For MaterialApp you have to use IconButton, for CupertinoApp you have to use CupertinoButton but instead of icon use child

Related

Globally remove all drop shadows in Flutter app?

Is there a way to globally remove all drop shadows in Flutter app?
I would like to do that in single place instead of setting elevation: 0 for all MaterialButtons, ElevatedButtons, etc.
I would like set theme, or do it another way, but globally in single palce.
I was looking for attributes in ThemeData, but can't find desired attributes, e.g. for MaterialButtons.
You were on the right track, ThemeData has an attribute for ElevatedButtons, i have made a small example on what you need to remove shadows based on MaterialStateProperty:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
//this is the function that deletes shadows
double getElevation(Set<MaterialState> states) {
return 0;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light().copyWith(
//this is the property that affects elevated buttons
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
elevation: MaterialStateProperty.resolveWith(getElevation)))),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
//this button doesn't have a shadow
return ElevatedButton(
child: const Text("HEY!"),
onPressed: () {},
);
}
}
The solution is pretty straightforward,
ThemeData(
shadowColor: Colors.transparent,
);
gets the job done

Show snack bar to handling missing URL receivers

I used the url_luncher package and it suggests handle the missing URL receiver in case of target platform can not handle the URL.
So I create a function to handle of onTap of CardTile widget and dial the phone number and if the target platform can not handle the request it shows a snake bar to inform the user in UI.
But I have two problems 1) if using an anonymous function I get a runtime error and my code would be wordly and long
Unhandled Exception: No ScaffoldMessenger widget found.
MyApp widgets require a ScaffoldMessenger widget ancestor.
The specific widget that could not find a ScaffoldMessenger ancestor was:
MyApp
The ancestors of this widget were:
[root]
Typically, the ScaffoldMessenger widget is introduced by the MaterialApp at the top of your application widget tree.
if use function name onTap of CardTile widget for example onTap : _urlLauncherFunction(context) I can not pass BuildContext context argument to the function and get a compile error
This expression has a type of 'void' so its value can't be used.
I could not figure out what did wrong so please guide and help me to solve this.
I paste the anonymous function version here
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(MyApp());
}
final telLaunchErrorSnackBar = SnackBar(
content:
Text('Your device can not handle this url'));
final String _myPhoneNumber = '+9812345678';
//use xml scheme to trigger error otherwise it should be tel
final Uri _telUri = Uri(scheme: 'xml', path: _myPhoneNumber);
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Column(
children: [
CircleAvatar(
backgroundImage: AssetImage('images/image.jpg'),
),
Card(
child: ListTile(
leading: Icon(
Icons.phone,
),
title: Text(
'00 123 45657',
),
onTap: () async {
String telUri = _telUri.toString();
await canLaunch(telUri)
? await launch(telUri)
: ScaffoldMessenger.of(context)
.showSnackBar(telLaunchErrorSnackBar);
},
),
),
],
),
),
),
);
}
}
When I code as this , flutter throw a error.
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(
primarySwatch: Colors.blue,
),
// home: MyHomePage(title: 'Flutter Demo Home Page'),
home: Scaffold(
body: SafeArea(
child: Container(
alignment: Alignment.center,
child: GestureDetector(
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
onTap: (){
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('content')));
},
),
),
),
),
);
}
}
then I change
...
MaterialApp(
home:Scafford(...),
)
...
to
...
MaterialApp(
home:Home(),
)
...
class Home extends StatelessWidget {
#override
Widget build(BuildContext context){
return Scaffold(...);
}
}
It works.

In flutter How to change FAB (Floating Action Button) custom animation Icon

I am new in flutter and now i am stuck to change FAB animation.Actually i am trying to before i press FAB that time it hadd add Icon and after press FAB it change icon close insted of add icon.
i provide one animation gif file link to more understand if any one know the solution please suggest me to solve this problem.
Here is the link https://miro.medium.com/max/468/1*qGa6VU4grjqEwAOScRm9BA.gif
In this link provided animation is showing that before press it shows the menu option icon and after press it show close icon but i want add option instead of menu option.
like add_close not a menu_close animationIcon.
I hope you understand my problem and suggest me
I think this code fulfill your requirements.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter FAB Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("FAB"),
),
floatingActionButton: AnimatedIconButton(
size: 30,
onPressed: () {
},
duration: Duration(milliseconds: 200),
endIcon: Icon(
Icons.close,
color: Colors.white,
),
startIcon: Icon(
Icons.add,
color: Colors.white,
),
startBackgroundColor: Colors.blue,
endBackgroundColor: Colors.blue,
),
);
}
}
this is the namespace which i used:
import 'package:flutter/material.dart';
import 'package:animated_icon_button/animated_icon_button.dart';
This code will work for all your requirements such as animation,multiple fab buttons with on pressed and also support images as a fab icon.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter FAB Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: UniWidget(),
);
}
}
class UniWidget extends StatefulWidget {
#override
_UniWidgetState createState() => _UniWidgetState();
}
class _UniWidgetState extends State<UniWidget> {
#override
Widget build(BuildContext context) {
var childButtons = List<UnicornButton>();
childButtons.add(UnicornButton(
hasLabel: false,
currentButton: FloatingActionButton(
backgroundColor: Colors.blue,
mini: false,
child: Padding(
padding: const EdgeInsets.all(15),
child: Image.asset('assets/images/arrow.png'),
),
onPressed: () {
print('scanbar');
},
)));
childButtons.add(UnicornButton(
hasLabel: false,
currentButton: FloatingActionButton(
backgroundColor: Colors.blue,
mini: false,
child: Padding(
padding: const EdgeInsets.all(15),
child: Image.asset('assets/images/contact.png'),
),
onPressed: () {
print('Contact');
},
)));
return Scaffold(
floatingActionButton: UnicornDialer(
parentButtonBackground: Colors.blue,
orientation: UnicornOrientation.VERTICAL,
childPadding: 10.0,
parentButton: Icon(Icons.add),
childButtons: childButtons),
appBar: AppBar(
title: Text("Fab demo"),
),
body: Center());
}
}
here is the namespaces
import 'package:flutter/material.dart';
import 'package:unicorndial/unicorndial.dart';
I hope it will fulfill your all type of requirements and work well in your project.

Leading on AppBar Incorrectly Persisting on Different Pages

I am working on a project in Flutter. The issue is that when I navigate to a different page, and then go back, there is an icon that I used a while ago while testing that appears on the home page's appbar. I went back into the code, and there wasn't leading within that page's appbar.
Here is a screenshot after a complete rebuild of the app.
After navigating to another page using Navigator.of(context).pushNamed('/streaks/edit/'):
And finally, navigating back to the home page using Navigator.of(context).pushNamed('/streaks/all/'):
main.dart code:
import 'package:flutter/material.dart';
import 'package:streaks/pages/all_streaks/all_streaks.dart';
import 'package:streaks/pages/edit_streak/edit_streak.dart';
void main() => runApp(StreaksApp());
class StreaksApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'streaks',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/streaks/all/',
routes: <String, WidgetBuilder>{
'/streaks/all/': (context) => AllStreaksPage(),
'/streaks/edit/': (context) => EditStreakPage(),
},
);
}
}
home page code (/streaks/all/):
import 'package:flutter/material.dart';
import 'streak_list.dart';
class AllStreaksPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('All Streaks'),
),
body: StreakList(),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).pushNamed('/streaks/edit/');
},
child: Icon(Icons.add, size: 40),
),
);
}
}
and the Edit Streak page (/streaks/edit/):
import 'package:flutter/material.dart';
import 'package:streaks/pages/edit_streak/streak_form.dart';
class EditStreakPage extends StatefulWidget {
#override
_EditStreakPageState createState() => _EditStreakPageState();
}
class _EditStreakPageState extends State<EditStreakPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.close, color: Theme.of(context).buttonColor),
onPressed: () {
Navigator.pushNamed(context, '/streaks/all/');
}),
title: Text('Edit Streak'),
),
body: StreakForm()
);
}
}
pubspec.yaml:
name: streaks
description: An app to boost your productivity with event organization.
publish_to: "none"
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
sqflite:
path:
flutter_iconpicker: ^2.1.5
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
Note: there aren't any errors logged into the debug console at any time.
Another Note: even after deleting the app and running again, there error persisted.
Please help. I was thinking it might be (a) a caching error or (b) there is a problem with stateless widgets as pages.
1st way:
In your EditStreakPage class, instead of using Navigator.pushNamed(context, '/streaks/all/'); use Navigator.pop(context)
Complete class code:
import 'package:flutter/material.dart';
import 'package:streaks/pages/edit_streak/streak_form.dart';
class EditStreakPage extends StatefulWidget {
#override
_EditStreakPageState createState() => _EditStreakPageState();
}
class _EditStreakPageState extends State<EditStreakPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.close, color: Theme.of(context).buttonColor),
onPressed: () {
Navigator.pop(context); // <-- use this intead
}),
title: Text('Edit Streak'),
),
body: StreakForm()
);
}
}
2nd way:
In your AllStreaksPage class, pass the property automaticallyImplyLeading: false to the AppBar()
Complete class code:
import 'package:flutter/material.dart';
import 'streak_list.dart';
class AllStreaksPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false; // <--- add this
title: Text('All Streaks'),
),
body: StreakList(),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).pushNamed('/streaks/edit/');
},
child: Icon(Icons.add, size: 40),
),
);
}
}
From what i understand you are saying that actions is appering when you Open YOur App -> EDitStreak Page->press the close button on top in editStreak page ..
The key reason for this is that instead of going back you are actually going to that page by navigating to it . in which the back button would lead you back to edit Streaks page .
Instead of Navigating You Should use
Navigator.of(context).pop()
and that should solve your issue , let me know in comments , if it helps

CupertinoDynamicColor not working for text style within a theme in Flutter

Currently I've got a problem with the dark mode for iOS in Flutter. I'm using a CupertinoApp with a CupertinoTheme.
With CupertinoDynamicColor.withBrightness() it's possible to define colors for dark and light theme. This is working for example for the navigation bar (barBackgroundColor), but not for the textTheme.
Someone already ask a similar question in the flutter repository but I don't know how to do this with a theme. Maybe you can help me there.
Example code:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(
barBackgroundColor: const CupertinoDynamicColor.withBrightness(
color: Colors.blue,
darkColor: Colors.red,
),
textTheme: CupertinoTextThemeData(
textStyle: TextStyle(
color: const CupertinoDynamicColor.withBrightness(
color: Colors.blue,
darkColor: Colors.red,
),
),
),
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(middle: Text('Test')),
child: Text('Test Text'),
);
}
}