Flutter Crop Image in jigsaw puzzle shape and use GridView.builder to build the layout

650 Views Asked by At

I've searched everywhere and have not so much experience in ClipPath I want is to make a layout similar to a Jigsaw Puzzle. I've already used Draggable and DragTarget for the text kind of puzzle but I want to render the image in the shape of a jigsaw and put the data empty until I drop a widget similar to that shape from the listview.builder, I found not many resources on this topic. below are the code and the layout I want to build.

the UI enter image description here main.dart

import 'package:flutter/material.dart';

import 'data_model.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Puzzle Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Puzzle Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String text = '';
  List<DataModel> dataModel = [];
  List<DataModel> dataModel2 = [];
  int rows = 4, columns = 4;

  void _incrementCounter() {
    for (var i = 1; i <= rows * columns; i++) {
      dataModel.add(DataModel(text: 'Hello', number: i));
    }
  }

  @override
  void initState() {
    super.initState();
    _incrementCounter();
    for (var model in dataModel) {
      dataModel2.add(DataModel(text: model.text, number: model.number));
    }
    dataModel2.shuffle();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: GridView.builder(
                  itemCount: dataModel.length,
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: rows,
                      mainAxisSpacing: 5,
                      crossAxisSpacing: 5),
                  itemBuilder: (context, index) {
                    return Center(
                      child: DragTarget<DataModel>(
                        builder: (BuildContext context,
                            List<Object?> candidateData,
                            List<dynamic> rejectedData) {
                          return Container(
                            color: dataModel[index].dataModel == null
                                ? Colors.red
                                : Colors.green,
                            child: Center(
                              child: Text(
                                dataModel[index].dataModel == null
                                    ? 'No Data'
                                    : '${dataModel[index].dataModel!.text} ${dataModel[index].dataModel!.number}',
                                style: TextStyle(
                                    color: dataModel[index].dataModel == null
                                        ? null
                                        : Colors.white,
                                    fontWeight: FontWeight.bold),
                              ),
                            ),
                          );
                        },
                        onAccept: (data) {
                          setState(() {
                            if (data.number == dataModel[index].number) {
                              dataModel[index].dataModel = data;
                              dataModel2.remove(data);
                            }
                          });
                        },
                        onWillAccept: (data) {
                          return data?.number == dataModel[index].number;
                        },
                      ),
                    );
                  }),
            ),
          ),
          SizedBox(
            height: 50,
            child: ListView.builder(
              itemCount: dataModel2.length,
              scrollDirection: Axis.horizontal,
              shrinkWrap: true,
              itemBuilder: (BuildContext context, int index) {
                return Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: LongPressDraggable<DataModel>(
                    data: dataModel2[index],
                    feedback: Material(
                      child: Text(
                        "${dataModel2[index].text} ${dataModel2[index].number}",
                        style: const TextStyle(fontWeight: FontWeight.bold),
                      ),
                    ),
                    // This is what shows while dragging the widget
                    childWhenDragging: Container(),
                    // This is what shows up when dragging
                    child: Text(
                        "${dataModel2[index].text} ${dataModel2[index].number}"), // This is the widget you drag
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

datamodel

class DataModel {
  String text;
  int number;
  DataModel? dataModel;

  DataModel({
    required this.text,
    required this.number,
    this.dataModel,
  });
}

0

There are 0 best solutions below