Flutter skeleton view shimmer view

1.8k views Asked by At

I'm trying to achieve the skeleton view as shown in the attachment but couldn't get it exactly like shown, i have tried Shimmer package, the problem is gradient or mask width is more. Kindly help with sample for to achieve the same.enter image description here

Used the shimmer: ^2.0.0

My ListView

return Padding(
    padding: const EdgeInsets.only(top: 16),
    child: ListView.builder(
      physics: const NeverScrollableScrollPhysics(),
      itemBuilder: (context, index) {
        return ShimmerItem();
      },
      itemCount: 10,
    ),
  );

ShimmerItem Widget:

class ShimmerItem extends StatelessWidget {
const ShimmerItem({Key? key}) : super(key: key);
@override
 Widget build(BuildContext context) {
  return Card(
   margin: EdgeInsets.only(left: 16, right: 16, bottom: 16),
   shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(10),
  ),
  color: Theme.of(context).cardTheme.color,
  child: Container(
    margin: EdgeInsets.only(left: 16, bottom: 8, top: 8),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
            padding: const EdgeInsets.only(right: 32.0),
            child: ShimmerWidget.rectangular(
              height: 16,
              width: MediaQuery.of(context).size.width * 0.9,
            )),
        Padding(
          padding: const EdgeInsets.only(right: 32.0, top: 8),
          child: ShimmerWidget.rectangular(
            height: 16,
            width: MediaQuery.of(context).size.width * 0.6,
          ),
        ),
        Padding(
          padding: const EdgeInsets.only(top: 16, bottom: 8.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              ShimmerWidget.rectangular(
                height: 16,
                width: MediaQuery.of(context).size.width * 0.4,
              ),
              SizedBox(
                width: 10,
              ),
              ShimmerWidget.rectangular(
                height: 16,
                width: MediaQuery.of(context).size.width * 0.4,
              ),
            ],
          ),
        ),
      ],
    ),
  ),
);
}
}

ShimmerWidget Class:

class ShimmerWidget extends StatelessWidget {
 final double width;
 final double height;
 final ShapeBorder shapeBorder;

 const ShimmerWidget.rectangular(
  {this.width = double.infinity, required this.height})
  : this.shapeBorder = const RoundedRectangleBorder();

 const ShimmerWidget.circular(
  {this.width = double.infinity,
  required this.height,
  this.shapeBorder = const CircleBorder()});

  @override
    Widget build(BuildContext context) => Shimmer.fromColors(
     baseColor:
                                                              
  ColorHelper.colorWithTransparency(AppColors.shimmer_bg_color, 
  100),
    highlightColor:
        
ColorHelper.colorWithTransparency(AppColors.shimmer_color_dark, 
  20),
    period: Duration(milliseconds: 1500),
    child: Container(
      width: width,
      height: height,
      decoration: ShapeDecoration(
        color: AppColors.shimmer_bg_color,
        shape: shapeBorder,
      ),
    ),
  );
 }
2

There are 2 answers

1
Md. Yeasin Sheikh On

We can use LayoutBuilder for measurement, which is depending on parent size and perfect for this case.

To struct the same UI, I am just constructing the ShimmerItem widget, rest of widget will be same.

Resource about layout-basics.

class ShimmerItem extends StatelessWidget {
  const ShimmerItem({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    final _border = BorderRadius.circular(12);
    final double _bHeight = 24;
    return LayoutBuilder(
      builder: (context, constraints) => Padding(
        padding: const EdgeInsets.all(12.0), //outside the card
        child: Container(
          decoration: ShapeDecoration(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(16),
            ),
            color: Colors.grey,
          ),
          padding: const EdgeInsets.all(8), // inner padding
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              //* top large two
              ClipRRect(
                borderRadius: _border,
                child: ShimmerWidget.rectangular(
                  height: _bHeight,
                  width: constraints.maxWidth,
                ),
              ),
              const SizedBox(height: 8),
              ClipRRect(
                borderRadius: _border,
                child: ShimmerWidget.rectangular(
                  height: _bHeight * .75,
                  width: constraints.maxWidth,
                ),
              ),
              const SizedBox(height: 8),
              //3rd row
              ClipRRect(
                borderRadius: _border,
                child: ShimmerWidget.rectangular(
                  height: _bHeight * .75,
                  width: constraints.maxWidth * 0.6,
                ),
              ),
              const SizedBox(height: 16),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  ClipRRect(
                    borderRadius: _border,
                    child: ShimmerWidget.rectangular(
                      height: _bHeight,
                      width: constraints.maxWidth * 0.4,
                    ),
                  ),
                  ClipRRect(
                    borderRadius: _border,
                    child: ShimmerWidget.rectangular(
                      height: _bHeight,
                      width: constraints.maxWidth * 0.4,
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

I am using demo color.

enter image description here

0
Tuan On

You need wrap all your child inside 1 bigger Shimmer wrapper to do that, not Shimmer on every small retangle.

I have project code for this in my laptop but im working afar, sorry.