Flutter Drag&Drop
Bu yazımda size aşağıdaki örnek üzerinden Flutterda drag&drop deyimini anlatacağım.
Haydi başlayalım…
Merhaba, Drag&Drop yani sürükle bırak diye bilinen, web ve mobilde bir çok uygulamada karşımıza çıkan bir kullanıcı deneyiminim Flutter’da nasıl yapıldığını kısaca kısa örneklerle açıklayıp yukarıdaki örnek uygulamayı sizlere anlatmaya çalışacağım.
Sürükle ve bırak (Drag and Drop), bir nesneyi parmağınızla veya fare ile tutulup , onu farklı bir konuma sürükleyip bıraktığınız ana kadar gelişen süreçtir. Süreç olarak tanımlamamın nedeni, bu işlem sırasında bir çok durum ortaya çıkabilir. Örneğin, kullanıcı sürükleme işleminden vazgeçebilir, hedef konum hariç farklı bir noktaya bırakma işlemi yapılabilir vs. Bu süreci daha iyi anlamak için kod üzerinden tüm durumları anlatmaya çalışacağım.
Peki Flutterda bu işlem nasıl olur?
Flutterda bir çok yardımcı widget olduğu gibi bu işlemi yapmak için Draggable widgeti kullanabiliriz. Bu widget sayesinde tüm süreci kolaylıkla yönetebilirsiniz.
Draggable widgetinin tüm detaylarına ve ek özelliklerine aşağıdaki linkten ulaşabilirsiniz.
Draggable class — widgets library — Dart API
Draggable widgetinin kullanımı
1- ilk olarak Draggable widgetini kullanarak sürükleme adımlarını öğrenelim
Yukarıdaki örnekte görüldüğü üzere bir nesneyi sürükle bırak yöntemiyle hareket ettiriliyor. Aşağıdaki kod bloğununda Draggable widgeti’nin standart özellikleri kullanılmıştır.
import 'package:flutter/material.dart';
class SampleDragging extends StatefulWidget {
const SampleDragging({Key? key}) : super(key: key);
@override
_SampleDraggingState createState() => _SampleDraggingState();
}
class _SampleDraggingState extends State<SampleDragging> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Draggable<String>(
data: 'ball',
child: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/ball.png'),
),
),
feedback: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/ball.png'),
),
),
/*childWhenDragging: const SizedBox(),
childWhenDragging: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/ball_gray.png'),
),
),*/
),
));
}
}
Kodu incedikten sonra önemli kısımları adım adım inceleyelim;
Draggable
:Scaffold
'in body’sineCenter
widgetinin child’i olarak Draggable widgetini ekliyoruz.data
: Draggable widgetinin içerisinde yer alan, hareket etmesini istediğimiz widgeti temsil eder. Bu widgetler DragTarget tarafından kullanılır.
Örnekte ben bir Sizebox içerisine bir Image ekledim.
feedback
: Bu kısımdata
widgetinin, yani sürüklemeye başladığınız ana widget’in referansını temsil eder. Hareket ettirmeye başladığınız anda boyutunu, rengini veya diğer özelliklerini değiştirerek kullanıcılara daha iyi bir deneyim sunabilirsiniz. Sürüklenen nesnenin mevcut konumundan ayrıldığını daha iyi gösterebilmek için referans objenin alfa değerini değiştirebilirsiniz veya farklı bir görselle değiştirebilirsiniz.
👀 2 durumda bunu inceleyelim;
childWhenDragging: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/ball_gray.png'),
),
childWhenDragging widgetimizi hareket ettirdiğimizde nereden hareket ettiğini kullanıcıya göstermek için kullanabiliriz.
childWhenDragging: const SizedBox()
Arkada kalan Objemizi gizlemek için Direk bir boş SizeBox ekleyebilirsiniz.
childWhenDragging
Direk boş bir size box eklerseniz göründü bu şekilde olacaktır.
axis: Axis.vertical
, Axis.horizontal
: Draggable widget’in axis özelliğini değiştirerek hareket yönünü yatay veya dikey yapabilirsiniz.
Sürükle- Bırak sürecinde Draggable widgeti bize bir durumu hazır olarak sunar. Bunlar;
onDragStarted
,onDragEnd
,onDraggableCanceled
,onDragCompleted
onDragStarted
: Sürükleme işlemi başladığı anı dinleyebiliriz.onDragEnd
: Sürükleme işlemi bittiği anda ekranın neresine bırakıldı, başarılı mı başarısız mı gibi durumları dinleyebilirizonDraggableCanceled
: Sürükleme işlemi başarısız olduğunda veya kullanıcı parmağını veya fareyi kaldırdığı anı dinlememizi sağlaronDragCompleted
: Sürükleme işleminin ilgili noktaya başarılı bir şekilde bırakıldığını dinlemizi sağlar
Flutterda sürüklenen bir widget’in hedef noktaya ulaştırmayı yönetmemizi sağlayan DragTarget
isiminde widget kullanılmaktadır.
özetle;
DragTarget
widgeti Draggable
widgetlerini kabul eden widgettir.
Aşağıdaki örnekte görüldüğü üzere
Kırmızı, Mavi ve Sarı balıklar Draggable
widgetlerimiz. aşağıda gördüğümüz akvaryum iseDragTarget
widgetimizi
import 'package:flutter/material.dart';
class SampleDragAndDrop extends StatefulWidget {
const SampleDragAndDrop({Key? key}) : super(key: key);
@override
_SampleDragAndDropState createState() => _SampleDragAndDropState();
}
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
class _SampleDragAndDropState extends State<SampleDragAndDrop> {
bool _isDropped = false;
String yellow = 'yellow';
String blue = 'blue';
String red = 'red';
var addedItems = <String>[];
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: const Icon(
Icons.reset_tv,
color: Colors.white,
),
onPressed: () {
clearAll();
},
)
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Visibility(
visible: !addedItems.contains(blue),
child: Draggable<String>(
// Data is the value this Draggable stores.
data: 'blue',
child: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/blue.png'),
),
),
feedback: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/blue.png'),
),
),
childWhenDragging: const SizedBox(
width: 120,
height: 120,
),
),
),
Visibility(
visible: !addedItems.contains(red),
child: Draggable<String>(
// Data is the value this Draggable stores.
data: 'red',
child: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/red.png'),
),
),
feedback: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/red.png'),
),
),
childWhenDragging: const SizedBox(
width: 120,
height: 120,
),
),
),
Visibility(
visible: !addedItems.contains(yellow),
child: Draggable<String>(
// Data is the value this Draggable stores.
data: 'yellow',
child: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/yellow.png'),
),
),
feedback: SizedBox(
height: 120.0,
width: 120.0,
child: Center(
child: Image.asset('assets/images/yellow.png'),
),
),
childWhenDragging: const SizedBox(
width: 120,
height: 120,
),
),
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.15,
),
DragTarget<String>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return SizedBox(
height: 320,
width: 320,
child: Center(
child: getBow(addedItems),
),
);
},
onWillAccept: (data) {
if (data == 'blue' || data == 'yellow' || data == 'red') {
return true;
} else {
return false;
}
},
onAccept: (data) {
setState(() {
if (!addedItems.contains(data)) {
addedItems.add(data);
}
_isDropped = true;
});
},
onLeave: (data) {},
),
],
),
)),
);
}
void clearAll() {
setState(() {
addedItems.clear();
});
}
Image getBow(List<String> selectedData) {if (selectedData.contains(yellow) &&
selectedData.contains(blue) &&
selectedData.contains(red)) {
return Image.asset('assets/images/full_bowl.png');
} else if (selectedData.contains(yellow) && selectedData.contains(blue)) {
return Image.asset('assets/images/without_red.png');
} else if (selectedData.contains(yellow) && selectedData.contains(red)) {
return Image.asset('assets/images/without_blue.png');
} else if (selectedData.contains(red) && selectedData.contains(blue)) {
return Image.asset('assets/images/without_yellow.png');
} else if (selectedData.contains(red)) {
return Image.asset('assets/images/red_bowl.png');
} else if (selectedData.contains(yellow)) {
return Image.asset('assets/images/yellow_bowl.png');
} else if (selectedData.contains(blue)) {
return Image.asset('assets/images/blue_bowl.png');
} else {
return Image.asset('assets/images/empty_bowl.png');
}
}
}
onWillAccept
is called whenever the item is dropped over theDragTarget
. We can use this method to retrieve the data carried by theDraggable
widget and decide whether to accept the item or not. In the code above, we accept the tomato image if it carries that string asred
onAccept
is a callback that we should receive once the item is accepted by theDragTarget
. We are showing the success message and updating the_isDropped
variable._isDropped
is used to change the image of the bowl to show the tomato inside the bowl image
Kısaca Flutterda sürükle bırakma işlemini 2 basit örnekle anlatmaya çalıştım. Siz de bu örneklerden yararlanacak puzzle gibi, sepete bir şeyler eklemek gibi güzel oyunlar bile yapabilirsiniz.
Projenin tamamına git hesabımdan erişebilirsiniz.