1

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(),
             },
    );
  }
}
Andre Clements
  • 634
  • 1
  • 7
  • 15
  • 1
    Does this answer your question? [No Material widget found](https://stackoverflow.com/questions/43947552/no-material-widget-found) – Ivo May 04 '22 at 12:46
  • no, been through that and another simmilar question and answers but it doesn't seem to be the same issue (e.g. I tried inserting explicit Material Widget in the stack etc.) - but initially there is no error, only at or after the the navigator pops, 'it' doesn't seem to see the material widget (or Scaffold) 'above' the InkWell at that point. – Andre Clements May 04 '22 at 12:47
  • Thanks @IvoBeckers, that made me go back and try more variations. I found that wrapping the button's widgets in a Material widget right above the InkWell widget does seem to work - I thought I had tried that... So what does work is in the MenuButton class: `Widget build(BuildContext context) { return Hero( child: Material( child: InkWell( child: Ink.image(` (But even though I have iut working now I'd still like to understand why that is neccesary with the Hero animation specifically, so I don't think I should mark this question as answered just yet. – Andre Clements May 04 '22 at 13:15
  • 1
    It may be helpful to post your routes Map or onGenerateRoute, in case the issue is tied to a navigation issue. – a.shak May 04 '22 at 13:54
  • Thanks @a.shak - updating the OP to include it. I was actually just experimenting with trying to build the routes map from/with a list but gave up on that. – Andre Clements May 04 '22 at 14:36

1 Answers1

2

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.

Dabbel
  • 2,468
  • 1
  • 8
  • 25
  • Thanks, but the Inkwell was inside a Scaffold - which works at initiation time, but the button is an instance of a class - so I suppose that means the hierarchy of the ancestry only goes up to the level of the definition of the that class, and not 'up' through it's instantiation? – Andre Clements May 04 '22 at 15:29
  • 1
    @AndreClements Probably depends on the internals of how Flutter does its Widget repaints upon navigation internally. As you wrote in one of your comments: Adding Material inside MenuButton solved it. This hints to the ancestry barrier you mentioned although it comes not always into play. – Dabbel May 04 '22 at 15:47