If I just throw together an image and some text in a rounded-corner rectangle, the user will not know that they can "click here". But I don't have to bake my own solution. InkWell covers this scenario, complete with a nice shadow.

I am positioning a custom clickable icon using the InkWell class, itself requiring to be inside an Ink instance.

shadow in front

import 'package:flutter/material.dart';

const boat_url = ('https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/'
    'Segelboot_Bodensee_Mainau_%28Foto_Hilarmont%29.JPG/'
    '182px-Segelboot_Bodensee_Mainau_%28Foto_Hilarmont%29.JPG');

void main() {
  runApp(MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Image',
      home: Scaffold(
        backgroundColor: Colors.grey,
        body: MyImage(),
      )));
}

class MyImage extends StatelessWidget {
  MyImage({Key key,});

  @override
  Widget build(BuildContext context) {
    Size sz = MediaQuery.of(context).size * 0.4;
    double border = 4;

    return Stack(children: [
      Positioned(
          top: 100,
          left: 100,
          width: sz.width,
          height: sz.height,
          child: Material(
            child: Ink(
              decoration: BoxDecoration(
                boxShadow: <BoxShadow>[
                  new BoxShadow(
                    color: Colors.red,
                    blurRadius: 10.0,
                    offset: new Offset(30.0, 20.0),
                  ),
                ],
                border: Border.all(
                  color: Colors.blue,
                  width: border,
                ),
                borderRadius: BorderRadius.circular(40),
              ),
              child: InkWell(
                onTap: (){/*..*/},
                child: Column(
                    children: [
                      Container(
                        height: 4 * (sz.height - 2 * border) / 5,
                        alignment: Alignment.center,
                        child: Image.network(boat_url),
                      ),
                      Container(
                        height: (sz.height - 2 * border) / 5,
                        child: FittedBox(
                            clipBehavior: Clip.antiAlias,
                            alignment: Alignment.centerLeft,
                            fit: BoxFit.fitHeight,
                            child: Text('A long descriptive sentence')),
                      )
                    ],
                  )),
            ),
          )),
    ]);
  }
}

1- I'm not actually using Colors.white, and the Scaffold itself has backgroundColor: Colors.grey. Where is the white background coming from?

2- When we talk of a "shadow", I'm expecting the shadow to be behind the ink/inkwell object. Why does the shadow appear in front?

Related: 1


Solution 1: Mohan Sai Manthri

That white color is from the Material widget, to remove that you can use type param.

Material(
      type: MaterialType.transparency,
      child: Container(),
    );

Here is code to achieve the custom button

Video link

Scaffold(
          backgroundColor: Colors.blueGrey,
          body: SafeArea(
            child: Container(
              decoration: BoxDecoration(
                  color: Colors.green.shade200,
                  border: Border.all(color: Colors.green),
                  borderRadius: BorderRadius.circular(5),
                  boxShadow: [
                    BoxShadow(
                      blurRadius: 5,
                      spreadRadius: 2,
                      color: Colors.black26,
                    )
                  ]),
              margin: const EdgeInsets.all(20),
              child: Material(
                type: MaterialType.transparency,
                child: InkWell(
                  onTap: () {},
                  splashColor: Colors.black26,
                  child: IntrinsicHeight(
                    child: Padding(
                      padding: const EdgeInsets.all(12.0),
                      child: Column(mainAxisSize: MainAxisSize.min, children: [
                        Image.asset(
                          'assets/images/marked_tyre_base.png',
                          fit: BoxFit.cover,
                          width: 80,
                          height: 80,
                        ),
                        const SizedBox(
                          height: 10,
                        ),
                        Text(
                          'Tyre 1',
                          style: TextStyle(color: Colors.white),
                        )
                      ]),
                    ),
                  ),
                ),
              ),
            ),
          ),
        );

enter image description here


Solution 2: CopsOnRoad

Screenshot:

enter image description here


Create a class, ImageTextButton:

class ImageTextButton extends StatelessWidget {
  final VoidCallback onPressed;
  final ImageProvider image;
  final double imageHeight;
  final double radius;
  final Widget text;

  ImageTextButton({
    @required this.onPressed,
    @required this.image,
    this.imageHeight = 200,
    this.radius = 28,
    @required this.text,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 8,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius)),
      clipBehavior: Clip.hardEdge,
      child: InkWell(
        onTap: onPressed,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Ink.image(
              image: image,
              height: imageHeight,
              fit: BoxFit.cover,
            ),
            SizedBox(height: 6),
            text,
            SizedBox(height: 6),
          ],
        ),
      ),
    );
  }
}

Usage:

ImageTextButton(
  onPressed: () {},
  image: AssetImage('chocolate_image'),
  text: Text('Chocolate'),
)