Flutter ve Firebase ile Uygulama Geliştirme: İslam Sanatları Projesi
Öğrencilerimizin hem görsel hem de ruhsal dünyasına hitap edecek, aynı zamanda modern bir altyapı (Flutter & Firebase) ile çalışacak bu uygulama.
Uygulama tasarımında İslam sanatlarına (Hüsn-i Hat, Ebru, Tezhip, Mimari) uygun estetik renkler (zümrüt yeşili, altın sarısı ve krem) kullandık. Uygulama; Firestore'dan verileri gerçek zamanlı çekecek bir Ana Sayfa, içeriklerin detaylı okunduğu bir Detay Sayfası ve sizin (veya görevli öğrencilerin) sisteme yeni içerik girebilmesi için bir İçerik Ekleme Sayfası barındırıyor.
Hazırladığımız bu projeyi, okuldaki laboratuvar bilgisayarlarına veya Pardus ETAP yüklü akıllı tahtalara (Cinnamon masaüstü ortamına) doğrudan yerel bir Linux uygulaması olarak kurmak, öğrencilerin erişimini çok daha kolaylaştıracaktır.
Değerler eğitimi kapsamında geliştirdiğimiz "İslamda Sanat ve Estetik" projesini, okullarımızda yaygın olarak kullanılan yerli işletim sistemimiz Pardus üzerine kurarak yerel bir Linux masaüstü uygulaması haline getirebiliriz. Pardus'un (özellikle ETAP sürümlerinin) sunduğu Cinnamon masaüstü ortamında, Flutter uygulamaları son derece performanslı ve akıcı çalışmaktadır.
1. Gerekli Sistem Bağımlılıklarının Kurulması
Flutter'ın Linux üzerinde masaüstü uygulaması (executable) derleyebilmesi için C++ derleme araçlarına ve GTK kütüphanelerine ihtiyacı vardır. Pardus terminalini (Ctrl+Alt+T) açın ve aşağıdaki komutları sırasıyla çalıştırın:
# Sistem paket listesini güncelleyelim
sudo apt update
# Flutter ve Linux Desktop derlemesi için gerekli temel araçları kuralım
sudo apt install -y curl git unzip xz-utils zip libglu1-mesa
# Linux masaüstü (GTK) bağımlılıklarını kuralım
sudo apt install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev
2. Flutter SDK Kurulumu
Pardus'ta Flutter SDK'yı kurmanın en sağlıklı yolu, doğrudan resmi GitHub deposundan kararlı (stable) sürümü çekmektir. Laboratuvar bilgisayarlarında herkesin (veya yönetici hesabının) erişebileceği bir dizine kurulum yapalım:
# Geliştirme araçları için bir klasör oluşturalım
mkdir -p ~/development
cd ~/development
# Flutter'ın kararlı sürümünü indirelim
git clone https://github.com/flutter/flutter.git -b stable
# Flutter komutlarını sistem yoluna (PATH) ekleyelim (Pardus varsayılan olarak Bash kullanır)
echo 'export PATH="$PATH:$HOME/development/flutter/bin"' >> ~/.bashrc
# Değişiklikleri mevcut terminale uygulayalım
source ~/.bashrc
Kurulumun doğruluğunu ve Linux masaüstü desteğinin aktif olup olmadığını kontrol edelim:
# Linux masaüstü desteğini aktif edelim
flutter config --enable-linux-desktop
# Flutter doktoru ile eksik var mı kontrol edelim
flutter doctor
(Not: Android Studio uyarıları alabilirsiniz, ancak sadece Linux masaüstü uygulaması derleyeceğimiz için bu uyarıları şimdilik göz ardı edebilirsiniz.)
3. Firebase ve FlutterFire CLI Kurulumu
Projemiz arka planda Firestore kullandığı için Firebase araçlarını kurmamız gerekiyor:
# Node.js tabanlı Firebase CLI'ı kuralım
curl -sL https://firebase.tools | bash
# Firebase hesabımıza giriş yapalım (Tarayıcı açılacaktır)
firebase login
# FlutterFire aracını aktif edelim
dart pub global activate flutterfire_cli
# Dart araçlarını PATH'e ekleyelim
echo 'export PATH="$PATH":"$HOME/.pub-cache/bin"' >> ~/.bashrc
source ~/.bashrc
Harika bir değerler eğitimi projesi. Öğrencilerinizin hem görsel hem de ruhsal dünyasına hitap edecek, aynı zamanda modern bir altyapı (Flutter & Firebase) ile çalışacak bu uygulama için ihtiyacınız olan tüm kodları adım adım hazırladım.
Uygulama tasarımında İslam sanatlarına (Hüsn-i Hat, Ebru, Tezhip, Mimari) uygun estetik renkler (zümrüt yeşili, altın sarısı ve krem) kullandık. Uygulama; Firestore'dan verileri gerçek zamanlı çekecek bir Ana Sayfa, içeriklerin detaylı okunduğu bir Detay Sayfası ve sizin (veya görevli öğrencilerin) sisteme yeni içerik girebilmesi için bir İçerik Ekleme Sayfası barındırıyor.
Proje Oluşturma ve Paketleri Ekleme
Öncelikle terminalden yeni projemizi oluşturup içine girelim:
flutter create islam_sanati_app
cd islam_sanati_app
Firebase CLI ayarlarını az önce çözdüğümüz şekilde yapalım (Terminalde bu komutları çalıştırıp projenizi seçin):
flutterfire configure
Gerekli paketleri projeye dahil edelim:
flutter pub add firebase_core cloud_firestore
Klasör Yapısı
Kodların daha düzenli olması ve öğrencilere modern yazılım mimarisini göstermek adına lib klasörü içinde şöyle bir yapı kuracağız:
lib/main.dart(Ana giriş ve tema)lib/models/art_model.dart(Veri modelimiz)lib/screens/home_screen.dart(Liste ekranı)lib/screens/detail_screen.dart(Okuma ekranı)lib/screens/add_content_screen.dart(Veri ekleme ekranı)
Kodların Tamamı
Aşağıdaki kodları ilgili dosyaları oluşturarak yapıştırabilirsiniz.
lib/models/art_model.dart
Veritabanından gelecek verileri nesneye dönüştüreceğimiz model sınıfımız.
import 'package:cloud_firestore/cloud_firestore.dart';
class ArtModel {
final String id;
final String title;
final String description;
final String category;
final String imageUrl;
ArtModel({
required this.id,
required this.title,
required this.description,
required this.category,
required this.imageUrl,
});
// Firestore'dan gelen veriyi modele çevirme
factory ArtModel.fromFirestore(DocumentSnapshot doc) {
Map data = doc.data() as Map<String, dynamic>;
return ArtModel(
id: doc.id,
title: data['title'] ?? '',
description: data['description'] ?? '',
category: data['category'] ?? '',
imageUrl: data['imageUrl'] ?? '',
);
}
// Modeli Firestore'a yazılacak formata çevirme
Map<String, dynamic> toMap() {
return {
'title': title,
'description': description,
'category': category,
'imageUrl': imageUrl,
'timestamp': FieldValue.serverTimestamp(),
};
}
}
lib/main.dart
Uygulamanın başlangıç noktası ve teması.
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'screens/home_screen.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const IslamSanatiApp());
}
class IslamSanatiApp extends StatelessWidget {
const IslamSanatiApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'İslamda Sanat ve Estetik',
debugShowCheckedModeBanner: false,
theme: ThemeData(
// Estetik renk paleti (Zümrüt Yeşili ve Altın)
primaryColor: const Color(0xFF0F4C3A),
scaffoldBackgroundColor: const Color(0xFFF9F6F0),
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF0F4C3A),
foregroundColor: Colors.white,
centerTitle: true,
elevation: 0,
),
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF0F4C3A),
secondary: const Color(0xFFD4AF37), // Altın Sarısı
),
),
home: const HomeScreen(),
);
}
}
lib/screens/home_screen.dart
Öğrencilerin içerikleri göreceği ana liste ekranı. Firestore'dan verileri gerçek zamanlı çeker.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/art_model.dart';
import 'detail_screen.dart';
import 'add_content_screen.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('İslam Sanatları', style: TextStyle(fontFamily: 'serif')),
actions: [
IconButton(
icon: const Icon(Icons.add_circle_outline),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const AddContentScreen()),
);
},
)
],
),
body: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('arts')
.orderBy('timestamp', descending: true)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
return const Center(
child: Text('Henüz içerik eklenmemiş.',
style: TextStyle(fontSize: 18, color: Colors.grey)));
}
return ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
ArtModel art = ArtModel.fromFirestore(snapshot.data!.docs[index]);
return Card(
elevation: 4,
margin: const EdgeInsets.only(bottom: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen(art: art)),
);
},
child: Row(
children: [
// Görsel Alanı
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
bottomLeft: Radius.circular(15),
),
child: art.imageUrl.isNotEmpty
? Image.network(
art.imageUrl,
width: 120,
height: 120,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
_buildPlaceholder(),
)
: _buildPlaceholder(),
),
// Metin Alanı
Expanded(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
art.category,
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
const SizedBox(height: 4),
Text(
art.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF2C3E50),
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
)
],
),
),
);
},
);
},
),
);
}
Widget _buildPlaceholder() {
return Container(
width: 120,
height: 120,
color: Colors.grey[300],
child: const Icon(Icons.image_not_supported, color: Colors.grey),
);
}
}
lib/screens/detail_screen.dart
Seçilen sanat dalının veya eserin detaylı olarak okunacağı, estetik görünümü ön planda tutan sayfa.
import 'package:flutter/material.dart';
import '../models/art_model.dart';
class DetailScreen extends StatelessWidget {
final ArtModel art;
const DetailScreen({super.key, required this.art});
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 250.0,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text(
art.title,
style: const TextStyle(
color: Colors.white,
shadows: [Shadow(color: Colors.black, blurRadius: 10)],
),
),
background: art.imageUrl.isNotEmpty
? Image.network(art.imageUrl, fit: BoxFit.cover)
: Container(color: Theme.of(context).primaryColor),
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Chip(
label: Text(art.category),
backgroundColor: Theme.of(context).colorScheme.secondary.withOpacity(0.2),
labelStyle: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
Text(
art.description,
style: const TextStyle(
fontSize: 16,
height: 1.6, // Okunabilirliği artırmak için satır aralığı
color: Color(0xFF34495E),
),
),
],
),
),
)
],
),
);
}
}
lib/screens/add_content_screen.dart
Uygulamayı içerikle doldurmak için kullanacağınız form sayfası. CRUD işlemlerinin "Create" (Oluşturma) adımı.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AddContentScreen extends StatefulWidget {
const AddContentScreen({super.key});
@override
State<AddContentScreen> createState() => _AddContentScreenState();
}
class _AddContentScreenState extends State<AddContentScreen> {
final _formKey = GlobalKey<FormState>();
String _title = '';
String _category = 'Hüsn-i Hat';
String _description = '';
String _imageUrl = '';
bool _isLoading = false;
final List<String> _categories = ['Hüsn-i Hat', 'Ebru', 'Tezhip', 'Mimari', 'Minyatür', 'Çini'];
Future<void> _saveContent() async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
setState(() => _isLoading = true);
try {
await FirebaseFirestore.instance.collection('arts').add({
'title': _title,
'category': _category,
'description': _description,
'imageUrl': _imageUrl,
'timestamp': FieldValue.serverTimestamp(),
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('İçerik başarıyla eklendi!')),
);
Navigator.pop(context); // İşlem bitince önceki sayfaya dön
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Hata oluştu: $e')),
);
} finally {
if (mounted) {
setState(() => _isLoading = false);
}
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Yeni İçerik Ekle')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DropdownButtonFormField<String>(
value: _category,
decoration: const InputDecoration(labelText: 'Kategori', border: OutlineInputBorder()),
items: _categories.map((cat) {
return DropdownMenuItem(value: cat, child: Text(cat));
}).toList(),
onChanged: (val) => setState(() => _category = val!),
),
const SizedBox(height: 16),
TextFormField(
decoration: const InputDecoration(labelText: 'Başlık', border: OutlineInputBorder()),
validator: (val) => val!.isEmpty ? 'Başlık boş bırakılamaz' : null,
onSaved: (val) => _title = val!,
),
const SizedBox(height: 16),
TextFormField(
decoration: const InputDecoration(labelText: 'Görsel URL (İsteğe Bağlı)', border: OutlineInputBorder()),
onSaved: (val) => _imageUrl = val ?? '',
),
const SizedBox(height: 16),
TextFormField(
maxLines: 8,
decoration: const InputDecoration(labelText: 'İçerik / Açıklama', border: OutlineInputBorder()),
validator: (val) => val!.isEmpty ? 'İçerik boş bırakılamaz' : null,
onSaved: (val) => _description = val!,
),
const SizedBox(height: 24),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.secondary,
padding: const EdgeInsets.symmetric(vertical: 16),
),
onPressed: _isLoading ? null : _saveContent,
child: _isLoading
? const CircularProgressIndicator(color: Colors.white)
: const Text('Kaydet', style: TextStyle(fontSize: 18, color: Colors.white)),
),
],
),
),
),
);
}
}
4. Firestore Veritabanı Kuralları
Kodları çalıştırıp test etmeden önce, Firebase Konsolunda "Firestore Database" sekmesine gidip veritabanını oluşturun. Öğrencileriniz verileri görebilsin, siz de veri ekleyebilin diye geçici olarak test modunda başlatabilirsiniz. "Rules" (Kurallar) sekmesindeki kuralları geliştirme aşamasında şu şekilde güncelleyebilirsiniz:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
// Geçici olarak herkese okuma/yazma izni verir. (Projeyi yayına alırken burayı güncelleyeceğiz)
allow read, write: if true;
}
}
}
Bu temel yapı, hem eğitim materyali sunmak hem de sınıf içi projelerde öğrencilere arka plan servislerinin nasıl senkronize çalıştığını göstermek için harika bir başlangıç noktasıdır. Uygulamayı çalıştırdıktan sonra "Ekle" butonuna basarak örnek bir "Ebru Sanatı" veya "Süleymaniye Camii" başlığı girip test edebilirsiniz.
Kaynak Kodlar : https://github.com/nuritiras/islam_sanati_app
4. Projenin Derlenmesi ve Çalıştırılması
Önceki adımlarda hazırladığımız "İslam Sanatı" projesini Pardus üzerinde derlemeye hazırız. Proje klasörünüzün içine terminalden girin:
cd ~/islam_sanati_app
# Gerekli Flutter paketlerini indirelim
flutter pub get
# Firebase yapılandırmasını Linux'u da kapsayacak şekilde güncelleyelim
# (Gelen ekranda projenizi seçip platformlardan 'linux'u işaretlemeyi unutmayın)
flutterfire configure
# Uygulamamızı Pardus (Linux) üzerinde çalıştıralım!
flutter run -d linux
Uygulamanız başarıyla açıldığında, Firebase Firestore'dan verilerin gerçek zamanlı olarak Pardus masaüstünüze geldiğini göreceksiniz.
5. Pardus Cinnamon İçin Uygulama Kısayolu Oluşturma (Bonus)
Derlediğimiz uygulamayı her seferinde terminalden başlatmak yerine, Cinnamon masaüstü ortamının uygulama menüsüne eklemek çok daha profesyonel bir çözümdür.
Öncelikle uygulamanın nihai (Release) sürümünü derleyelim:
flutter build linux
Bu komut, uygulamayı build/linux/x64/release/bundle/ dizininde çalıştırılabilir bir dosya olarak oluşturur.
Şimdi Cinnamon uygulama menüsü için bir .desktop dosyası oluşturalım:
nano ~/.local/share/applications/islam-sanati.desktop
Açılan nano editörüne şu metni yapıştırın (dosya yolundaki KULLANICI_ADINIZ kısmını kendi oturum adınıza göre değiştirmeyi unutmayın):
[Desktop Entry]
Version=1.0
Type=Application
Name=İslamda Sanat ve Estetik
Comment=Değerler Eğitimi Uygulaması
Exec=/home/KULLANICI_ADINIZ/islam_sanati_app/build/linux/x64/release/bundle/islam_sanati_app
Icon=utilities-terminal
Terminal=false
Categories=Education;
Ctrl+O, Enter ve Ctrl+X tuş kombinasyonlarıyla dosyayı kaydedip çıkın. Artık uygulamanız Pardus'un başlat menüsünde Eğitim kategorisi altında "İslamda Sanat ve Estetik" adıyla görünecek ve tek tıklamayla çalışacaktır!
Bu adımları takip ederek projeyi okuldaki tüm Pardus bilgisayarlara kolayca entegre edebilirsiniz.
Yorumlar
Yorum Gönder