Flutter Newb: I have a grid of image buttons on a menu screen. When clicking a button it animates into the 'leading' element of the new view's AppBar. That works as I was expecting it to. So far so good...
When I Navigator.pop() that 2nd view I get the error "No Material widget found."... The specific widget that could not find a Material ancestor was: _InkResponseStateWidget... (though the menu screen does reappear as expected
/// ...
class MenuScreen extends StatelessWidget {
const MenuScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Menu'),
),
body: Center(
child: GridView.count(
crossAxisCount: 3,
children: const [
MenuButton('clients'),
//...and so on
///...
class MenuButton extends StatelessWidget {
final String indexStr;
const MenuButton(this.indexStr, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Hero(
child: InkWell(
child: Ink.image(
image: AssetImage('assets/images/$indexStr.png'),
fit: BoxFit.cover,
),
onTap: () => Navigator.pushNamed(context, '/' + indexStr),
highlightColor: Colors.blue.withOpacity(0.5),
splashColor: Colors.blue.withOpacity(0.9),
borderRadius: BorderRadius.circular(100),
),
tag: indexStr,
);
}
}
PS. I can't say I completely understand all the context and key stuff either :-)
The second 'screen's code is
import 'package:flutter/material.dart';
class ClientsScreen extends StatelessWidget {
const ClientsScreen({Key? key}) : super(key: key);
final indexStr = 'clients';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Material(
child: Hero(
child: Image.asset('../assets/images/$indexStr.png'),
tag: indexStr,
),
),
title: const Text('Clients.'),
),
body: Center(child: Text('Test')),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Back')),
);
}
}
I imagine I'm missing something obvious, but can't see what it might be...
Update:, and the main.dart root app with the routes map:
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Named Routes',
theme: ThemeData(scaffoldBackgroundColor: Colors.white),
initialRoute: '/',
routes: {
'/': (context) => const MenuScreen(),
'/clients': (context) => const ClientsScreen(),
'/checkin': (context) => const CheckinScreen(),
},
);
}
}
As others already pointed out in the comments, a Material
widget is needed to wrap the InkWell
.
Reason:
The InkWell widget must have a Material widget as an ancestor. The Material widget is where the ink reactions are actually painted. This matches the material design premise wherein the Material is what is actually reacting to touches by spreading ink.
https://api.flutter.dev/flutter/material/InkWell-class.html
Ink effects: Material shows ink effects implemented by InkFeatures like InkSplash and InkHighlight below its children.
https://api.flutter.dev/flutter/material/Material-class.html
The Material
widget displays ink effects like InkSplash
and InkHighlight
of its respective children. Widgets already implementing the Material
functionality are Scaffold
, Card
etc. Which is why one of these is needed as an ancestor, and if not, Material
is needed.