1️⃣ 목표

flutter 라이브러리를 사용하여 파일 시스템에서 이미지를 받아온 뒤 이미지를 자른 후 화면에 노출시키기

2️⃣ 사용한 라이브러리

  1. image_picker_web: https://pub.dev/packages/image_picker_web
  2. image_cropper: https://pub.dev/packages/image_cropper

3️⃣ 기본 세팅

1. pubspec.yaml

1
2
3
4
5
6
dependencies:
  flutter:
    sdk: flutter
  image_picker_web: ^3.1.1
  http: ^0.13.5
  image_cropper: ^3.0.1

2. web/index.html

1
2
3
4
5
6
<head>
  <!-- Croppie -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.css" />
  <script defer src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.3.0/exif.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.js"></script>
</head>

star1 주의 사항

플러터 웹에서 로컬 파일 시스템에 직접 접근하는 것(File 형태로)은 보안상의 이유로 허용되지 않으므로 blob 형태의 url로 변환해야 함.

4️⃣ 전체 소스 코드 (main.dart)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import 'dart:html' as html;

import 'package:flutter/material.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker_web/image_picker_web.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 const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Image cropper! 🍏'),
    );
  }
}

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 croppedBlobUrl = '';
  String fileName = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: SingleChildScrollView(
        scrollDirection: Axis.vertical,
        child: Center(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Padding(
                padding: const EdgeInsets.all(15.0),
                child: SizedBox(
                  width: 300,
                  height: 300,
                  child: croppedBlobUrl != ''
                      ? CircleAvatar(
                          radius: 50, // 원하는 크기 지정
                          backgroundColor: Colors.transparent,
                          backgroundImage: NetworkImage(croppedBlobUrl),
                        )
                      : const Icon(Icons.disabled_by_default_rounded),
                ),
              ),
              ElevatedButton.icon(
                  onPressed: () async {
                    pickAndCropImage(context);
                  },
                  icon: const Icon(Icons.image),
                  label: const Text('pick an image from gallery'))
            ],
          ),
        ),
      ), 
    );
  }

  Future<void> pickAndCropImage(BuildContext context) async {
    html.File? imageFile = (await ImagePickerWeb.getMultiImagesAsFile())?[0];

    setState(() {
      if (imageFile != null) {
        fileName = imageFile.name;
        if (context.mounted) {
          cropImage(imageFile, context);
        }
      }
    });
  }

  Future<void> cropImage(html.File imageFile, context) async {
    final imageUrl = html.Url.createObjectUrlFromBlob(imageFile);
    final croppedFile = await ImageCropper().cropImage(
      sourcePath: imageUrl,
      aspectRatioPresets: [
        CropAspectRatioPreset.square,
        CropAspectRatioPreset.ratio3x2,
        CropAspectRatioPreset.original,
        CropAspectRatioPreset.ratio4x3,
        CropAspectRatioPreset.ratio16x9
      ],
      uiSettings: [WebUiSettings(context: context)],
    );

    html.Url.revokeObjectUrl(imageUrl);

    setState(() {
      if (croppedFile != null) {
        croppedBlobUrl = croppedFile.path;
      }
    });
  }
}

5️⃣ 동작 확인

1
2
flutter build web
flutter run -d chrome

6️⃣ 결과물

Leave a comment