I have recently started using Material ui (v5) and I am facing some issues with theme customization. I want to change the font-family for all the components through the ThemeProvider and creating a new theme. I am able to see the theme in the react inspector with the correct values but those values don't get applied to the components (specifically fontFamily but nothing works really).
This is what my ThemeProvider looks like:
import React from "react";
import {
createTheme,
ThemeProvider as MUIThemeProvider,
} from "#mui/material/styles";
const theme = createTheme({
typography: {
fontFamily: "serif",
},
});
type ThemeProviderProps = {
children: React.ReactNode;
};
const ThemeProvider = ({ children }: ThemeProviderProps) => (
<MUIThemeProvider theme={theme}>{children}</MUIThemeProvider>
);
export default ThemeProvider;
The ThemeProvider then wraps every story (using storybook):
<div
style={{
height: "100%",
width: "100%",
}}
>
<ThemeProvider>
<Story {...context} />
</ThemeProvider>
</div>
);
export const decorators = [withThemeProvider];```
I am able to see the theme I created if I use the useTheme hook or in the ThemeProvider in the components inspector (please ignore the different fontFamily in the screenshot below):
When I look at the actual component I see that it is still getting the default styles:
I am sure I am doing something stupid somewhere?
I have made customTheme using createMuiTheme() and I used it in the <ThemeProvider>.
Now, I want to make some custom styles using that customTheme inside the makeStyles() function. But makeStyles() is not getting my customTheme. It gets the default theme.
Code:
import React from 'react';
import { createMuiTheme, makeStyles, ThemeProvider } from '#material-ui/core';
const customTheme = createMuiTheme({
palette: {
primary: {
main: '#f0f'
},
}
});
const useStyles = makeStyles((theme) => ({
box: {
height: '100px',
width: '100px',
backgroundColor: theme.palette.primary.main
}
}));
export default function App() {
const classes = useStyles();
return (
<ThemeProvider theme={customTheme}>
<div className={classes.box}>
makeStyles
</div>
<div style={{
height: '100px',
width: '100px',
backgroundColor: customTheme.palette.primary.main
}}>
inline style
</div>
</ThemeProvider>
);
}
Screenshot:
Screenshot Image
As can see in the Screenshot, the first <div> has default deep blue color of Material-UI that is using makeStyles.
The second <div> has the custom color, that is using inline style.
So, how can I use the customTheme in makeStyles()?
Try ThemeProvider to wrap over App component, as per my understanding theme provided will be applied to react components wrapped under it. since "makeStyles div" is just an element styles didn't apply to it.
Codesandbox Link - https://codesandbox.io/s/mui-custom-theme-f354x
UPDATE (2021/05/11):
Flutter now natively has Hover Events implemented Widgets.
There is a MouseCursor for Widgets like RaisedButton and properties like hoverColor or hoverElevation.
https://api.flutter.dev/flutter/services/MouseCursor-class.html
You can also use an InkWell anywhere else as stated in the accepted answer.
Original Question:
How can the cursor appearance be changed within Flutter?
I know that with the Listener() Widget we can listen for Mouse-Events,
but I haven't found any information regarding hovering events for flutter web.
Has someone found a soulution yet?
I had difficulties finding documentation on the now built-in support. Here is what helped me: https://github.com/flutter/flutter/issues/58260
And this did the trick for me, without changing index.html etc.
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: Icon(
Icons.add_comment,
size: 20,
),
onTap: () {},
),
),
Also see the official documentation: https://api.flutter.dev/flutter/rendering/MouseCursor-class.html
Widget build(BuildContext context) {
return Center(
child: MouseRegion(
cursor: SystemMouseCursors.text,
child: Container(
width: 200,
height: 100,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.yellow),
),
),
),
);
}
And here https://api.flutter.dev/flutter/material/MaterialStateMouseCursor-class.html yet another wonderful example from the official docs that "...defines a mouse cursor that resolves to SystemMouseCursors.forbidden when its widget is disabled."
Starting with dev channel build version 1.19.0–3.0.pre there is built-in support for the pointer cursor. The same method as bellow is used with the difference that is applied to the Flutter app container element flt-glass-pane. Using the bellow method will just duplicate the behavior.
In order to override the pointer cursor, you can use the bellow method but applied on the flt-glass-pane element.
A workaround for this is the following:
You have to set an id (for example app-container on the entire body of the app's index.html template).
This is how your index.html will look like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My awesome app</title>
</head>
<body id="app-container">
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
Next, you have to create a wrapper dart class. I called it hand_cursor.dart:
import 'package:flutter_web/gestures.dart';
import 'package:flutter_web/widgets.dart';
import 'package:universal_html/html.dart' as html;
// see https://pub.dev/packages/universal_html
class HandCursor extends MouseRegion {
// get a reference to the body element that we previously altered
static final appContainer = html.window.document.getElementById('app-container');
HandCursor({Widget child}) : super(
onHover: (PointerHoverEvent evt) {
appContainer.style.cursor='pointer';
// you can use any of these:
// 'help', 'wait', 'move', 'crosshair', 'text' or 'pointer'
// more options/details here: http://www.javascripter.net/faq/stylesc.htm
},
onExit: (PointerExitEvent evt) {
// set cursor's style 'default' to return it to the original state
appContainer.style.cursor='default';
},
child: child
);
}
After that, wherever you want to have the hand cursor shown, you have to wrap your element in this HandCursor wrapper. See the class awesome_button.dart bellow:
import 'package:awesome_app/widgets/containers/hand_cursor.dart';
import 'package:flutter_web/material.dart';
import 'package:flutter_web/widgets.dart';
class AwesomeButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
HandCursor(
child: IconButton(
onPressed: () {
// do some magic
},
icon: Icon(Icons.star)
),
)
],
);
}
}
A short explanation can be found here.
A more versatile update, that works on the new web projects created with the master channel of Flutter, can be found here.
I hope it helps.
You can use an InkWell that has an onHover event
InkWell(
onTap: () {},
onHover: (value) {
setState(() {
isHovered = value;
});
},
child: Container(
width: 50,
height: 72,
color: Colors.black
)
);
Make sure to have something onTap, even an empty function, else it is considered to be disabled, and the hover won't work
The previous method is deprecated. Here is the updated code
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'package:universal_html/prefer_sdk/html.dart' as html;
class HandCursor extends MouseRegion {
static final appContainer = html.window.document.getElementById('app-container');
HandCursor({Widget child})
: super(
onHover: (PointerHoverEvent evt) {
appContainer.style.cursor = 'pointer';
},
onExit: (PointerExitEvent evt) {
appContainer.style.cursor = 'default';
},
child: child,
);
}
And in your pubspec.yaml file, add universal_html as a package as a dependency. The version may change.
dependencies:
flutter:
sdk: flutter
universal_html: ^1.1.4
You still want to have an id of app-container attached to the body of your html. Here is my html file.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Your App Title</title>
</head>
<body id="app-container">
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
You want to put the code for the HandCursor widget in its own file. You can call it hand_cursor.dart. And to use it on the widget you want the hand to show up on, import it into the file you're working on and wrap the widget you want in the HandCursor widget.
From Flutter beta version 1.19.0-4.1.pre, add id to body and set cursor of that doesn't work. Because flt-glass-pane is replacing the cursor.
So the solution is that set cursor directly to flt-glass-pane.
Below is the update that is working.
class HandCursor extends MouseRegion {
static final appContainer = html.window.document.querySelectorAll('flt-glass-pane')[0];
HandCursor({Widget child}) : super(
onHover: (PointerHoverEvent evt) {
appContainer.style.cursor='pointer';
},
onExit: (PointerExitEvent evt) {
appContainer.style.cursor='default';
},
child: child
);
}
final appContainer
= html.document.getElementsByTagName('body')[0] as html.Element;
GestureDetector(
child: MouseRegion(
child: Text(
'https://github.com/yumi0629',
style: textStyle,
),
onHover: (_) => appContainer.style.cursor = 'pointer',
onExit: (_) => appContainer.style.cursor = 'default',
),
onTap: () {
print('open');
js.context.callMethod(
'open', ['https://github.com/yumi0629']);
},
)
The most easy way what i know
InkWell(
onTap: (){},
mouseCursor: MaterialStateMouseCursor.clickable,
...
I believe that mouse events won't work on the web, Listener Widget was demoed on Google I/O 2019 and worked with mouse, but that was as a ChromeOS app and not a web app.
According to Flutter web on GitHub:
At this time, desktop UI interactions are not fully complete, so a UI built with flutter_web may feel like a mobile app, even when running on a desktop browser.
Adapted answer by Constantin Stan
For those who want to have the click effect similar to InkWell widget and with border radius option:
Add to your pubspec.yaml file
dependencies:
universal_html: ^1.1.4
Then add to the index.html file the following the tag <body id="app-container"> as below:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Your App Title</title>
</head>
<body id="app-container">
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
Finally create the following widget and use encapsulated all the necessary widgets:
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:universal_html/prefer_sdk/html.dart' as html;
class InkWellMouseRegion extends InkWell {
InkWellMouseRegion({
Key key,
#required Widget child,
#required GestureTapCallback onTap,
double borderRadius = 0,
}) : super(
key: key,
child: !kIsWeb ? child : HoverAware(child: child),
onTap: onTap,
borderRadius: BorderRadius.circular(borderRadius),
);
}
class HoverAware extends MouseRegion {
// get a reference to the body element that we previously altered
static final appContainer = html.window.document.getElementById('app-container');
HoverAware({Widget child}) : super(
onHover: (PointerHoverEvent evt) {
appContainer.style.cursor='pointer';
// you can use any of these:
// 'help', 'wait', 'move', 'crosshair', 'text' or 'pointer'
// more options/details here: http://www.javascripter.net/faq/stylesc.htm
},
onExit: (PointerExitEvent evt) {
// set cursor's style 'default' to return it to the original state
appContainer.style.cursor='default';
},
child: child
);
}
I'm trying to integrate material-ui to my project and I have some issues with custom theme settings
I created a custom theme this way
App.js
const theme = createMuiTheme({
palette: {
primary: green,
secondary: red,
},
});
class App extends Component {
render() {
return (
<MuiThemeProvider theme={theme}>
<BrowserRouter>
<Switch>
...}}
Then in a component in the substructure I create some specific css.
Now my issue is that I'm obliged to define style appBar with a backgroud color and apply it explicitely on the AppBar component. If I don't do one of these two operations, the bg of the appBar remains light gray
What is weired is that I get the correct green from theme.palette.primary["500"], which means the theme is correctly configured
Header.js
const styles = theme => ({
root: {
flexGrow: 1,
},
grow: {
flexGrow: 1,
},
appBar: {
backgroundColor: theme.palette.primary["500"]
},//...)}
class Header extends Component {
constructor(props) {
super(props)
this.classes = props.classes
}
render() {
return (<I18n>
{(tsl, {i18n, t, ready}) => {
return (
<div className={this.classes.root}>
<AppBar position="static" color="default" className={this.classes.appBar}>
<Toolbar>...(irrelevant code)
I followed the examples in https://material-ui.com/demos/app-bar/ where the first example has the light gray color, then all the other examples have a blue bg, but there's nothing in the source code that was added to apply the blue color (in my opinion)
Any help please? thanks folks
Use <AppBar position="static" color="primary">.
By default AppBar is using the colors from the grey palette.
We are considering defaulting to primary for the color prop since it's not following the spec and having grey as a default for something as prominent as the app bar is a bad idea anyway.
I looked through the doc and couldn't find an attribute z-index which defines the Drawer display style : over / beneath the AppBar ? Any quick ways to do that ?
Thx,
Here you go:
<Drawer
containerStyle={{zIndex: 1000}}
>
for some z-index != AppBar's
You can style the drawer and the app bar at the top-level of your app, using the ThemeProvider. There is a zIndex property which describes these values for different components.
import { ThemeProvider } from "#material-ui/styles";
import { createMuiTheme } from "#material-ui/core/styles";
const customTheme = createMuiTheme({
zIndex: {
appBar: 1100,
drawer: 1200
}
});
// Then, wrap your app with a ThemeProvider
<ThemeProvider theme={customTheme}>
<App />
</ThemeProvider>