1. Başlamadan önce
Bu codelab'de, Android ve iOS için Flutter mobil uygulamaları oluştururken Firebase ile ilgili bazı temel bilgileri öğreneceksiniz.
Ön koşullar
- Flutter hakkında bilgi sahibi olmak
- Flutter SDK'sı
- İstediğiniz metin düzenleyici
Neler öğreneceksiniz?
- Flutter ile Android, iOS, web ve macOS'te etkinlik LCV ve konuk defteri sohbet uygulaması oluşturma.
- Firebase Authentication ile kullanıcı kimliklerini doğrulama ve verileri Firestore ile senkronize etme.
Gerekenler
Aşağıdaki cihazlardan herhangi biri:
- Bilgisayarınıza bağlı ve geliştirici moduna ayarlanmış fiziksel bir Android veya iOS cihaz.
- iOS simülasyon aracı (Xcode araçları gerektirir).
- Android emülatörü (Android Studio'da kurulum gerektirir).
Ayrıca aşağıdakilere de ihtiyacınız vardır:
- Tercih ettiğiniz bir tarayıcı (ör. Google Chrome).
- Dart ve Flutter eklentileriyle yapılandırılmış bir IDE veya metin düzenleyici (ör. Android Studio veya Visual Studio Code).
- Flutter'ın en yeni
stable
sürümü veya uçta yaşamayı seviyorsanızbeta
. - Firebase projenizi oluşturmak ve yönetmek için bir Google Hesabı.
Firebase
CLI, Google Hesabınıza giriş yaptı.
2. Örnek kodu alın
GitHub'dan projenizin ilk sürümünü indirin:
- Komut satırından
flutter-codelabs
dizininde GitHub deposunu klonlayın:
git clone https://github.com/flutter/codelabs.git flutter-codelabs
flutter-codelabs
dizini, bir kod laboratuvarı koleksiyonunun kodunu içerir. Bu codelab'in kodu flutter-codelabs/firebase-get-to-know-flutter
dizinindedir. Dizin, projenizin her adımın sonunda nasıl görünmesi gerektiğini gösteren bir dizi anlık görüntü içerir. Örneğin, ikinci adımdasınız.
- İkinci adım için eşleşen dosyaları bulun:
cd flutter-codelabs/firebase-get-to-know-flutter/step_02
İleri gitmek veya bir adımdan sonra bir şeyin nasıl görünmesi gerektiğini görmek isterseniz, ilgilendiğiniz adımın adını taşıyan dizine bakın.
Başlangıç uygulamasını içe aktarın
flutter-codelabs/firebase-get-to-know-flutter/step_02
dizinini tercih ettiğiniz IDE'de açın veya içe aktarın. Bu dizin, henüz işlevsel olmayan bir Flutter buluşma uygulamasından oluşan kodlab'ın başlangıç kodunu içerir.
Üzerinde çalışılması gereken dosyaları bulma
Bu uygulamadaki kod birden fazla dizine yayılmış. Bu işlevsellik bölümü, kodu işleve göre gruplandırdığı için işi kolaylaştırır.
- Şu dosyaları bulun:
lib/main.dart
: Bu dosya, ana giriş noktasını ve uygulama widget'ını içerir.lib/home_page.dart
: Bu dosya ana sayfa widget'ını içerir.lib/src/widgets.dart
: Bu dosya, uygulamanın tarzını standartlaştırmaya yardımcı olacak birkaç widget içerir. Bu widget'lar, başlatıcı uygulamanın ekranını oluşturur.lib/src/authentication.dart
: Bu dosya, Firebase e-posta tabanlı kimlik doğrulaması için giriş kullanıcı deneyimi oluşturmak üzere bir dizi widget ile birlikte kısmi bir Kimlik Doğrulama uygulaması içerir. Kimlik doğrulama akışı için bu widget'lar henüz başlangıç uygulamasında kullanılmıyor, ancak yakında eklenecek.
Uygulamanın geri kalanını derlemek için gereken ek dosyaları eklersiniz.
lib/main.dart
dosyasını inceleyin
Bu uygulama, Roboto'yu uygulama genelinde varsayılan yazı tipi yapmak için google_fonts
paketinden yararlanır. fonts.google.com adresini keşfedebilir ve burada bulduğunuz yazı tiplerini uygulamanın farklı bölümlerinde kullanabilirsiniz.
lib/src/widgets.dart
dosyasındaki yardımcı widget'ları Header
, Paragraph
ve IconAndDetail
biçiminde kullanırsınız. Bu widget'lar, HomePage
bölümünde açıklanan sayfa düzenindeki dağınıklığı azaltmak için yinelenen kodu ortadan kaldırır. Bu aynı zamanda tutarlı bir görünüm ve tarz sağlar.
Uygulamanız Android, iOS, web ve macOS'te şu şekilde görünür:
3. Firebase projesi oluşturma ve yapılandırma
Etkinlik bilgilerinin görüntülenmesi konuklarınız için çok güzel olsa da, tek başına kimse çok faydalı olmaz. Uygulamaya bazı dinamik işlevler eklemeniz gerekir. Bunu yapmak için Firebase'i uygulamanıza bağlamanız gerekir. Firebase'i kullanmaya başlamak için bir Firebase projesi oluşturup yapılandırmanız gerekir.
Firebase projesi oluşturma
- Firebase'de oturum açın.
- Konsolda Proje Ekle veya Proje oluştur'u tıklayın.
- Proje adı alanına Firebase-Flutter-Codelab yazın ve Devam'ı tıklayın.
- Proje oluşturma seçeneklerini tıklayarak ilerleyin. İstenirse Firebase şartlarını kabul edin ancak bu uygulama için kullanmayacağınız için Google Analytics kurulumunu atlayın.
Firebase projeleri hakkında daha fazla bilgi için Firebase projelerini anlama başlıklı makaleyi inceleyin.
Uygulama, web uygulamaları için kullanılabilen aşağıdaki Firebase ürünlerini kullanır:
- Kimlik doğrulama: Kullanıcıların uygulamanızda oturum açmasına olanak tanır.
- Firestore: Yapılandırılmış verileri buluta kaydeder ve veriler değiştiğinde anlık bildirimler alır.
- Firebase Security Rules: Veritabanınızın güvenliğini sağlar.
Bu ürünlerden bazılarının özel yapılandırma gerektirmesi veya Firebase Konsolu'nda etkinleştirilmesi gerekir.
E-posta ile oturum açma kimlik doğrulamasını etkinleştirme
- Firebase konsolunun Projeye genel bakış bölmesinde Derleme menüsünü genişletin.
- Kimlik Doğrulama > Başla > Oturum açma yöntemi > E-posta/Şifre > Etkinleştir > Kaydet'i tıklayın.
Firestore kurulumu
Web uygulaması, sohbet mesajlarını kaydetmek ve yeni sohbet mesajları almak için Firestore'u kullanır.
Firebase projenizde Firestore'u ayarlamak için:
- Firebase konsolunun sol panelinde Derleme'yi genişletin ve ardından Firestore veritabanı'nı seçin.
- Create database'i (Veritabanı oluştur) tıklayın.
- Veritabanı Kimliği'ni
(default)
olarak bırakın. - Veritabanınız için bir konum seçip İleri'yi tıklayın.
Gerçek bir uygulama için kullanıcılarınıza yakın bir konum seçmeniz gerekir. - Test modunda başlat'ı tıklayın. Güvenlik kurallarıyla ilgili sorumluluk reddi beyanını okuyun.
Bu kod laboratuvarının ilerleyen bölümlerinde, verilerinizin güvenliğini sağlamak için güvenlik kuralları ekleyeceksiniz. Veritabanınıza Güvenlik Kuralları eklemeden bir uygulamayı dağıtmayın veya herkese açık olarak göstermeyin. - Oluştur'u tıklayın.
4. Firebase'i yapılandırma
Firebase'i Flutter ile kullanmak için Flutter projesini FlutterFire
kitaplıklarını doğru şekilde kullanacak şekilde yapılandırmak üzere aşağıdaki görevleri tamamlamanız gerekir:
- Projenize
FlutterFire
bağımlılıklarını ekleyin. - İstediğiniz platformu Firebase projesine kaydedin.
- Platforma özel yapılandırma dosyasını indirip koda ekleyin.
Flutter uygulamanızın en üst düzey dizininde, sırasıyla iOS ve Android için platforma özel yapılandırma dosyalarını barındıran android
, ios
, macos
ve web
alt dizinleri bulunur.
Bağımlılıkları yapılandırma
Bu uygulamada kullandığınız iki Firebase ürünü için FlutterFire
kitaplıklarını eklemeniz gerekir: Authentication ve Firestore.
- Komut satırından aşağıdaki bağımlılıkları ekleyin:
$ flutter pub add firebase_core
firebase_core
paketi, tüm Firebase Flutter eklentileri için gereken ortak koddur.
$ flutter pub add firebase_auth
firebase_auth
paketi, kimlik doğrulama ile entegrasyonu sağlar.
$ flutter pub add cloud_firestore
cloud_firestore
paketi, Firestore veri depolama alanına erişmenizi sağlar.
$ flutter pub add provider
firebase_ui_auth
paketi, kimlik doğrulama akışlarıyla geliştirici hızını artırmak için bir dizi widget ve yardımcı program sağlar.
$ flutter pub add firebase_ui_auth
Gerekli paketleri eklediniz. Ancak Firebase'i uygun şekilde kullanmak için iOS, Android, macOS ve Web çalıştırıcı projelerini de yapılandırmanız gerekir. Ayrıca, iş mantığının görüntüleme mantığından ayrılmasına olanak tanıyan provider
paketini de kullanırsınız.
FlutterFire CLI'yi yükleme
FlutterFire CLI, temel Firebase CLI'ye bağlıdır.
- Henüz yapmadıysanız makinenize Firebase CLI'yi yükleyin.
- FlutterFire CLI'yi yükleyin:
$ dart pub global activate flutterfire_cli
flutterfire
komutu yüklendiğinde genel olarak kullanılabilir.
Uygulamalarınızı yapılandırma
CLI, belirli bir platformun tüm yapılandırmasını oluşturmak için Firebase projenizden ve seçili proje uygulamalarından bilgi alır.
Uygulamanızın kökünde configure
komutunu çalıştırın:
$ flutterfire configure
Yapılandırma komutu, aşağıdaki işlemlerde size yol gösterir:
.firebaserc
dosyasına göre veya Firebase Konsolu'ndan bir Firebase projesi seçin.- Android, iOS, macOS ve web gibi yapılandırma platformlarını belirleyin.
- Yapılandırmanın ayıklanacağı Firebase uygulamalarını belirleyin. Varsayılan olarak CLI, Firebase uygulamalarını mevcut proje yapılandırmanıza göre otomatik olarak eşleştirmeye çalışır.
- Projenizde bir
firebase_options.dart
dosyası oluşturun.
macOS'i yapılandırın
macOS'te Flutter, tamamen korumalı alan içinde uygulamalar oluşturur. Bu uygulama Firebase sunucularıyla iletişim kurmak için ağla entegre olduğundan uygulamanızı ağ istemcisi ayrıcalıklarıyla yapılandırmanız gerekir.
macos/Runner/DebugProfile.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<!-- Add the following two lines -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
macos/Runner/Release.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<!-- Add the following two lines -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
Daha fazla bilgi edinmek için Flutter için masaüstü desteği başlıklı makaleye bakın.
5. LCV işlevi ekleme
Firebase'i uygulamaya eklediğinize göre, kullanıcıları Authentication ile kaydeden bir LCV yanıtı düğmesi oluşturabilirsiniz. Android yerel, iOS yerel ve web için önceden oluşturulmuş FirebaseUI Auth
paketleri mevcuttur ancak bu özelliği Flutter için oluşturmanız gerekir.
Daha önce aldığınız proje, kimlik doğrulama akışının büyük bir kısmı için kullanıcı arayüzünü uygulayan bir dizi widget içeriyordu. Kimlik doğrulamayı uygulamayla entegre etmek için iş mantığını uygularsınız.
Provider
paketiyle iş mantığı ekleme
Uygulamanın Flutter widget'ları ağacında merkezi bir uygulama durumu nesnesini kullanılabilir hale getirmek için provider
paketini kullanın:
- Aşağıdaki içeriğe sahip
app_state.dart
adlı yeni bir dosya oluşturun:
lib/app_state.dart
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
bool _loggedIn = false;
bool get loggedIn => _loggedIn;
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
FirebaseUIAuth.configureProviders([
EmailAuthProvider(),
]);
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loggedIn = true;
} else {
_loggedIn = false;
}
notifyListeners();
});
}
}
import
ifadeleri Firebase Core ve Auth'u tanıtır, uygulama durumu nesnesini widget ağacı genelinde kullanılabilir hale getiren provider
paketini getirir ve firebase_ui_auth
paketindeki kimlik doğrulama widget'larını içerir.
Bu ApplicationState
uygulama durumu nesnesinin bu adımdaki tek sorumluluğu, widget ağacını kimliği doğrulanmış durumda bir güncelleme olduğu konusunda uyarmaktır.
Sağlayıcıyı yalnızca kullanıcının giriş durumunu uygulamaya bildirmek için kullanırsınız. Kullanıcının giriş yapmasına izin vermek için firebase_ui_auth
paketi tarafından sağlanan kullanıcı arayüzlerini kullanırsınız. Bu, uygulamalarınızda giriş ekranlarını hızlı bir şekilde başlatmanın mükemmel bir yoludur.
Kimlik doğrulama akışını entegre etme
lib/main.dart
dosyasının üst kısmındaki içe aktarma işlemlerini değiştirin:
lib/main.dart
import 'package:firebase_ui_auth/firebase_ui_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; // new
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; // new
import 'app_state.dart'; // new
import 'home_page.dart';
- Uygulama durumunu uygulama başlatma işlemine bağlayın ve ardından kimlik doğrulama akışını
HomePage
'e ekleyin:
lib/main.dart
void main() {
// Modify from here...
WidgetsFlutterBinding.ensureInitialized();
runApp(ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: ((context, child) => const App()),
));
// ...to here.
}
main()
işlevinde yapılan değişiklik, uygulama durumu nesnesinin ChangeNotifierProvider
widget'ı ile oluşturulmasından sağlayıcı paketini sorumlu kılar. Bu özel provider
sınıfını, uygulama durumu nesnesi ChangeNotifier
sınıfını genişlettiği için kullanıyorsunuz. Bu sayede provider
paketi, bağımlı widget'ların ne zaman yeniden gösterileceğini bilir.
GoRouter
yapılandırması oluşturarak uygulamanızı, FirebaseUI'nin sizin için sağladığı farklı ekranlara gitme işlemini gerçekleştirecek şekilde güncelleyin:
lib/main.dart
// Add GoRouter configuration outside the App class
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
routes: [
GoRoute(
path: 'sign-in',
builder: (context, state) {
return SignInScreen(
actions: [
ForgotPasswordAction(((context, email) {
final uri = Uri(
path: '/sign-in/forgot-password',
queryParameters: <String, String?>{
'email': email,
},
);
context.push(uri.toString());
})),
AuthStateChangeAction(((context, state) {
final user = switch (state) {
SignedIn state => state.user,
UserCreated state => state.credential.user,
_ => null
};
if (user == null) {
return;
}
if (state is UserCreated) {
user.updateDisplayName(user.email!.split('@')[0]);
}
if (!user.emailVerified) {
user.sendEmailVerification();
const snackBar = SnackBar(
content: Text(
'Please check your email to verify your email address'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
context.pushReplacement('/');
})),
],
);
},
routes: [
GoRoute(
path: 'forgot-password',
builder: (context, state) {
final arguments = state.uri.queryParameters;
return ForgotPasswordScreen(
email: arguments['email'],
headerMaxExtent: 200,
);
},
),
],
),
GoRoute(
path: 'profile',
builder: (context, state) {
return ProfileScreen(
providers: const [],
actions: [
SignedOutAction((context) {
context.pushReplacement('/');
}),
],
);
},
),
],
),
],
);
// end of GoRouter configuration
// Change MaterialApp to MaterialApp.router and add the routerConfig
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Firebase Meetup',
theme: ThemeData(
buttonTheme: Theme.of(context).buttonTheme.copyWith(
highlightColor: Colors.deepPurple,
),
primarySwatch: Colors.deepPurple,
textTheme: GoogleFonts.robotoTextTheme(
Theme.of(context).textTheme,
),
visualDensity: VisualDensity.adaptivePlatformDensity,
useMaterial3: true,
),
routerConfig: _router, // new
);
}
}
Her ekranın, kimlik doğrulama akışının yeni durumuna bağlı olarak farklı bir işlem türü vardır. Kimlik doğrulamadaki çoğu durum değişikliğinden sonra, tercih ettiğiniz bir ekrana (ana ekran veya profil gibi farklı bir ekran) yeniden yönlendirme yapabilirsiniz.
HomePage
sınıfının derleme yönteminde, uygulama durumunuAuthFunc
widget'ıyla entegre edin:
lib/home_page.dart
import 'package:firebase_auth/firebase_auth.dart' // new
hide EmailAuthProvider, PhoneAuthProvider; // new
import 'package:flutter/material.dart'; // new
import 'package:provider/provider.dart'; // new
import 'app_state.dart'; // new
import 'src/authentication.dart'; // new
import 'src/widgets.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Firebase Meetup'),
),
body: ListView(
children: <Widget>[
Image.asset('assets/codelab.png'),
const SizedBox(height: 8),
const IconAndDetail(Icons.calendar_today, 'October 30'),
const IconAndDetail(Icons.location_city, 'San Francisco'),
// Add from here
Consumer<ApplicationState>(
builder: (context, appState, _) => AuthFunc(
loggedIn: appState.loggedIn,
signOut: () {
FirebaseAuth.instance.signOut();
}),
),
// to here
const Divider(
height: 8,
thickness: 1,
indent: 8,
endIndent: 8,
color: Colors.grey,
),
const Header("What we'll be doing"),
const Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
],
),
);
}
}
AuthFunc
widget'ını örneklendirdiniz ve bir Consumer
widget'ı içinde sarmaladınız. Tüketici widget'ı, uygulama durumu değiştiğinde ağacın bir bölümünü yeniden oluşturmak için provider
paketinin kullanılabileceği genel yöntemdir. AuthFunc
widget'ı, test ettiğiniz ek widget'lardır.
Kimlik doğrulama akışını test etme
- Uygulamada
SignInScreen
'yi başlatmak için LCV düğmesine dokunun.
- Bir e-posta adresi girin. Zaten kayıtlıysanız sistem sizden bir şifre girmenizi ister. Aksi takdirde sistem, kayıt formunu doldurmanızı ister.
- Hata işleme akışını kontrol etmek için altı karakterden kısa bir şifre girin. Kayıtlıysanız bunun yerine şifresini görürsünüz.
- Hata işleme akışını kontrol etmek için yanlış şifreler girin.
- Doğru şifreyi girin. Kullanıcıya çıkış seçeneği sunan giriş yapma deneyimini görürsünüz.
6. Firestore'a mesaj yazma
Kullanıcıların uygulamanızı ziyaret ettiğini bilmek güzel ancak konuklara uygulamada yapacak başka bir şey sunmanız gerekiyor. Misafir defterine mesaj bırakabilmeleri iyi olmaz mı? Davete katılmaktan neden heyecan duyduğunuzu veya kiminle tanışmayı umduğunuzu paylaşabilirsiniz.
Kullanıcıların uygulamada yazdığı sohbet mesajlarını depolamak için Firestore'u kullanırsınız.
Veri modeli
Firestore bir NoSQL veritabanıdır ve veritabanında depolanan veriler koleksiyonlara, belgelere, alanlara ve alt koleksiyonlara bölünür. Sohbetin her mesajını üst düzey bir koleksiyon olan guestbook
koleksiyonunda doküman olarak depolarsınız.
Firestore'a mesaj ekleme
Bu bölümde, kullanıcıların veritabanına mesaj yazmasını sağlayan işlevi eklersiniz. Önce bir form alanı ve gönder düğmesi, ardından bu öğeleri veritabanına bağlayan kodu ekleyin.
guest_book.dart
adlı yeni bir dosya oluşturun, bir mesaj alanının ve gönder düğmesinin kullanıcı arayüzü öğelerini oluşturmak için durum bilgisine sahip birGuestBook
widget ekleyin:
lib/guest_book.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'src/widgets.dart';
class GuestBook extends StatefulWidget {
const GuestBook({required this.addMessage, super.key});
final FutureOr<void> Function(String message) addMessage;
@override
State<GuestBook> createState() => _GuestBookState();
}
class _GuestBookState extends State<GuestBook> {
final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
final _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Row(
children: [
Expanded(
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Leave a message',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Enter your message to continue';
}
return null;
},
),
),
const SizedBox(width: 8),
StyledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
await widget.addMessage(_controller.text);
_controller.clear();
}
},
child: Row(
children: const [
Icon(Icons.send),
SizedBox(width: 4),
Text('SEND'),
],
),
),
],
),
),
);
}
}
Burada birkaç önemli yer var. İlk olarak, iletinin gerçekten içerik barındırdığını doğrulayabilmeniz için bir form somutlaştırırsınız ve hiç içerik yoksa kullanıcıya bir hata mesajı gösterirsiniz. Bir formu doğrulamak için formun arkasındaki form durumuna GlobalKey
ile erişirsiniz. Anahtarlar ve bunların nasıl kullanılacağı hakkında daha fazla bilgi için Anahtarları ne zaman kullanmalısınız? başlıklı makaleyi inceleyin.
Ayrıca widget'ların yerleşim şekline dikkat edin. TextFormField
içeren bir Row
ve Row
içeren bir StyledButton
'niz var. Ayrıca TextFormField
öğesinin bir Expanded
widget'ına sarmalandığını unutmayın. Bu, TextFormField
öğesinin satırdaki fazladan alanı doldurmasını sağlar. Bunun neden gerekli olduğunu daha iyi anlamak için Kısıtlamaları anlama bölümüne bakın.
Kullanıcının, ziyaretçi defterine eklemek için metin girmesine olanak tanıyan bir widget'ınız olduğuna göre bu widget'ı ekrana eklemeniz gerekir.
HomePage
öğesinin gövdesini düzenleyerekListView
öğesinin çocuklarının sonuna aşağıdaki iki satırı ekleyin:
const Header("What we'll be doing"),
const Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
// Add the following two lines.
const Header('Discussion'),
GuestBook(addMessage: (message) => print(message)),
Bu, widget'ı görüntülemek için yeterli olsa da yararlı bir şey yapmak için yeterli değildir. Çalışması için bu kodu kısa süre içinde güncellersiniz.
Uygulama önizlemesi
Kullanıcı GÖNDER'i tıkladığında aşağıdaki kod snippet'i tetiklenir. Mesaj giriş alanının içeriğini, veritabanının guestbook
koleksiyonuna ekler. Özellikle, addMessageToGuestBook
yöntemi, ileti içeriğini guestbook
koleksiyonunda otomatik olarak oluşturulmuş bir kimliğe sahip yeni bir dokümana ekler.
FirebaseAuth.instance.currentUser.uid
değerinin, Kimlik Doğrulaması'nın giriş yapmış tüm kullanıcılar için verdiği otomatik olarak oluşturulmuş benzersiz kimliğe bir referans olduğunu unutmayın.
lib/app_state.dart
dosyasınaaddMessageToGuestBook
yöntemini ekleyin. Bu özelliği bir sonraki adımda kullanıcı arayüzüne bağlayacaksınız.
lib/app_state.dart
import 'package:cloud_firestore/cloud_firestore.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
class ApplicationState extends ChangeNotifier {
// Current content of ApplicationState elided ...
// Add from here...
Future<DocumentReference> addMessageToGuestBook(String message) {
if (!_loggedIn) {
throw Exception('Must be logged in');
}
return FirebaseFirestore.instance
.collection('guestbook')
.add(<String, dynamic>{
'text': message,
'timestamp': DateTime.now().millisecondsSinceEpoch,
'name': FirebaseAuth.instance.currentUser!.displayName,
'userId': FirebaseAuth.instance.currentUser!.uid,
});
}
// ...to here.
}
Kullanıcı arayüzünü ve veritabanını bağlama
Kullanıcının Ziyaretçi Defteri'ne eklemek istediği metni girebileceği bir kullanıcı arayüzünüz vardır ve girişi Firestore'a eklemek için gereken koda sahipsiniz. Artık tek yapmanız gereken ikisini birbirine bağlamaktır.
lib/home_page.dart
dosyasındaHomePage
widget'ında aşağıdaki değişikliği yapın:
lib/home_page.dart
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'app_state.dart';
import 'guest_book.dart'; // new
import 'src/authentication.dart';
import 'src/widgets.dart';
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Firebase Meetup'),
),
body: ListView(
children: <Widget>[
Image.asset('assets/codelab.png'),
const SizedBox(height: 8),
const IconAndDetail(Icons.calendar_today, 'October 30'),
const IconAndDetail(Icons.location_city, 'San Francisco'),
Consumer<ApplicationState>(
builder: (context, appState, _) => AuthFunc(
loggedIn: appState.loggedIn,
signOut: () {
FirebaseAuth.instance.signOut();
}),
),
const Divider(
height: 8,
thickness: 1,
indent: 8,
endIndent: 8,
color: Colors.grey,
),
const Header("What we'll be doing"),
const Paragraph(
'Join us for a day full of Firebase Workshops and Pizza!',
),
// Modify from here...
Consumer<ApplicationState>(
builder: (context, appState, _) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (appState.loggedIn) ...[
const Header('Discussion'),
GuestBook(
addMessage: (message) =>
appState.addMessageToGuestBook(message),
),
],
],
),
),
// ...to here.
],
),
);
}
}
Bu adımın başında eklediğiniz iki satırı tam uygulamayla değiştirdiniz. Ağacın oluşturduğunuz kısmı için uygulama durumunu kullanılabilir hale getirmek için yine Consumer<ApplicationState>
yöntemini kullanırsınız. Bu sayede, kullanıcı arayüzüne bir mesaj giren birine tepki verebilir ve bu mesajı veritabanında yayınlayabilirsiniz. Sonraki bölümde, eklenen mesajların veritabanında yayınlanıp yayınlanmadığını test edersiniz.
Mesaj göndermeyi test etme
- Gerekirse uygulamada oturum açın.
Hey there!
gibi bir mesaj girin ve GÖNDER'i tıklayın.
Bu işlem, mesajı Firestore veritabanınıza yazar. Ancak, verilerin alınmasını uygulamanız gerektiği için mesajı gerçek Flutter uygulamanızda görmezsiniz. Bu işlemi bir sonraki adımda yaparsınız. Ancak Firebase konsolunun Veritabanı kontrol panelinde, eklediğiniz mesajı guestbook
koleksiyonunda görebilirsiniz. Daha fazla mesaj gönderirseniz guestbook
koleksiyonunuza daha fazla doküman eklemiş olursunuz. Örneğin, aşağıdaki kod snippet'ine bakın:
7. Mesajları okuma
Davetlilerin veritabanına mesaj yazabilmesi güzel bir özellik ancak henüz uygulamada göremiyorlar. Bu sorunu düzeltmenin zamanı geldi.
İletileri senkronize etme
Mesajları görüntülemek için veriler değiştiğinde tetiklenen dinleyiciler eklemeniz ve ardından yeni mesajları gösteren bir kullanıcı arayüzü öğesi oluşturmanız gerekir. Uygulama durumuna, uygulamadan yeni eklenen mesajları dinleyen bir kod eklersiniz.
- Yeni bir dosya (
guest_book_message.dart
) oluşturun. Firestore'da depoladığınız verilerin yapılandırılmış görünümünü ortaya çıkarmak için aşağıdaki sınıfı ekleyin.
lib/guest_book_message.dart
class GuestBookMessage {
GuestBookMessage({required this.name, required this.message});
final String name;
final String message;
}
lib/app_state.dart
dosyasına aşağıdaki içe aktarma işlemlerini ekleyin:
lib/app_state.dart
import 'dart:async'; // new
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
import 'guest_book_message.dart'; // new
- Durumu ve alıcıları tanımladığınız
ApplicationState
bölümünde aşağıdaki satırları ekleyin:
lib/app_state.dart
bool _loggedIn = false;
bool get loggedIn => _loggedIn;
// Add from here...
StreamSubscription<QuerySnapshot>? _guestBookSubscription;
List<GuestBookMessage> _guestBookMessages = [];
List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
// ...to here.
ApplicationState
işlevinin ilklendirme bölümünde, kullanıcı oturum açtığında doküman koleksiyonu üzerinden bir sorguya abone olmak ve oturumu kapattığında aboneliği iptal etmek için aşağıdaki satırları ekleyin:
lib/app_state.dart
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
FirebaseUIAuth.configureProviders([
EmailAuthProvider(),
]);
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loggedIn = true;
_guestBookSubscription = FirebaseFirestore.instance
.collection('guestbook')
.orderBy('timestamp', descending: true)
.snapshots()
.listen((snapshot) {
_guestBookMessages = [];
for (final document in snapshot.docs) {
_guestBookMessages.add(
GuestBookMessage(
name: document.data()['name'] as String,
message: document.data()['text'] as String,
),
);
}
notifyListeners();
});
} else {
_loggedIn = false;
_guestBookMessages = [];
_guestBookSubscription?.cancel();
}
notifyListeners();
});
}
Bu bölüm, guestbook
koleksiyonu için sorgu oluşturmak ve bu koleksiyona abone olma ve abonelikten çıkma işlemlerini gerçekleştirmek için kullanacağınız yer olduğundan önemlidir. Akışı dinlersiniz. Burada guestbook
koleksiyonundaki iletilerin yerel bir önbelleğini yeniden oluşturur ve daha sonra aboneliği iptal etmek için aboneliğin referans kaydını saklarsınız. Burada çok fazla işlem gerçekleşiyor. Bu nedenle, daha net bir zihinsel model elde etmek için neler olduğunu incelemek üzere bu kodu bir hata ayıklayıcıda keşfetmeniz gerekir. Daha fazla bilgi için Firestore ile gerçek zamanlı güncelleme alma başlıklı makaleyi inceleyin.
lib/guest_book.dart
dosyasına aşağıdaki içe aktarmayı ekleyin:
import 'guest_book_message.dart';
- Bu değişen durumu kullanıcı arayüzüne bağlamak için
GuestBook
widget'ına yapılandırmanın parçası olarak bir mesaj listesi ekleyin:
lib/guest_book.dart
class GuestBook extends StatefulWidget {
// Modify the following line:
const GuestBook({
super.key,
required this.addMessage,
required this.messages,
});
final FutureOr<void> Function(String message) addMessage;
final List<GuestBookMessage> messages; // new
@override
_GuestBookState createState() => _GuestBookState();
}
_GuestBookState
içinde, bu yapılandırmayı göstermek içinbuild
yöntemini aşağıdaki şekilde değiştirin:
lib/guest_book.dart
class _GuestBookState extends State<GuestBook> {
final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
final _controller = TextEditingController();
@override
// Modify from here...
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...to here.
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Row(
children: [
Expanded(
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Leave a message',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Enter your message to continue';
}
return null;
},
),
),
const SizedBox(width: 8),
StyledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
await widget.addMessage(_controller.text);
_controller.clear();
}
},
child: Row(
children: const [
Icon(Icons.send),
SizedBox(width: 4),
Text('SEND'),
],
),
),
],
),
),
),
// Modify from here...
const SizedBox(height: 8),
for (var message in widget.messages)
Paragraph('${message.name}: ${message.message}'),
const SizedBox(height: 8),
],
// ...to here.
);
}
}
build()
yönteminin önceki içeriğini Column
widget'ı ile sarmalarsınız ve ardından ileti listesindeki her ileti için yeni bir Paragraph
oluşturmak üzere Column
öğesinin alt öğelerinin kuyruğuna bir koleksiyon eklersiniz.
GuestBook
öğesini yenimessages
parametresiyle doğru şekilde oluşturmak içinHomePage
öğesinin gövdesini güncelleyin:
lib/home_page.dart
Consumer<ApplicationState>(
builder: (context, appState, _) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (appState.loggedIn) ...[
const Header('Discussion'),
GuestBook(
addMessage: (message) =>
appState.addMessageToGuestBook(message),
messages: appState.guestBookMessages, // new
),
],
],
),
),
Mesaj senkronizasyonunu test etme
Firestore, verileri veritabanına abone olan istemcilerle otomatik ve anında senkronize eder.
Mesaj senkronizasyonunu test edin:
- Uygulamada, veritabanında daha önce oluşturduğunuz mesajları bulun.
- Yeni mesajlar yazabilirsiniz. Anında görünürler.
- Çalışma alanınızı birden fazla pencerede veya sekmede açın. Mesajlar, pencereler ve sekmeler arasında gerçek zamanlı olarak senkronize edilir.
- İsteğe bağlı: Firebase konsolunun Veritabanı menüsünde manuel olarak iletileri silin, değiştirin veya yeni iletiler ekleyin. Tüm değişiklikler kullanıcı arayüzünde görünür.
Tebrikler! Uygulamanızda Firestore belgelerini okuyorsunuz.
Uygulama önizlemesi
8. Temel güvenlik kurallarını ayarlama
Başlangıçta Firestore'u test modunu kullanacak şekilde ayarlarsınız. Bu, veritabanınızın okuma ve yazma işlemleri için açık olduğu anlamına gelir. Ancak test modunu yalnızca geliştirmenin ilk aşamalarında kullanmalısınız. En iyi uygulama olarak, uygulamanızı geliştirirken veritabanınız için güvenlik kuralları oluşturmanız gerekir. Güvenlik, uygulamanızın yapısı ve davranışının ayrılmaz bir parçasıdır.
Firebase Güvenlik Kuralları, veritabanınızdaki dokümanlara ve koleksiyonlara erişimi kontrol etmenize olanak tanır. Esnek kurallar söz dizimi, veritabanının tamamına yazma işlemlerinden belirli bir dokümandaki işlemlere kadar her şeyle eşleşen kurallar oluşturmanıza olanak tanır.
Temel güvenlik kuralları oluşturun:
- Firebase konsolunun Geliştirme menüsünde, Veritabanı > Kurallar'ı tıklayın. Aşağıdaki varsayılan güvenlik kurallarını ve herkese açık kurallarla ilgili bir uyarı görürsünüz:
- Uygulamanın veri yazdığı koleksiyonları tanımlayın:
match /databases/{database}/documents
bölümünde, güvenliğini sağlamak istediğiniz koleksiyonu belirleyin:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /guestbook/{entry} {
// You'll add rules here in the next step.
}
}
Her bir konuk defteri belgesinde kimlik doğrulama UID'sini bir alan olarak kullandığınız için kimlik doğrulama UID'sini alabilir ve dokümana yazmaya çalışan herkesin eşleşen bir kimlik doğrulama UID'sine sahip olduğunu doğrulayabilirsiniz.
- Okuma ve yazma kurallarını kural grubunuza ekleyin:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /guestbook/{entry} {
allow read: if request.auth.uid != null;
allow write:
if request.auth.uid == request.resource.data.userId;
}
}
}
Artık yalnızca oturum açmış kullanıcılar konuk defterindeki mesajları okuyabilir ancak mesajları yalnızca mesajın yazarı düzenleyebilir.
- Beklenen tüm alanların dokümanda olduğundan emin olmak için veri doğrulaması ekleyin:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /guestbook/{entry} {
allow read: if request.auth.uid != null;
allow write:
if request.auth.uid == request.resource.data.userId
&& "name" in request.resource.data
&& "text" in request.resource.data
&& "timestamp" in request.resource.data;
}
}
}
9. Bonus adım: Öğrendiklerinizi uygulayın
Katılımcıların LCV durumlarını kaydetme
Uygulamanız şu anda kullanıcıların yalnızca etkinlikle ilgilendikleri anda sohbet etmesine olanak tanıyor. Ayrıca, bir kullanıcının sohbete gelip gelmediğini anlamanın tek yolu, söz konusu kullanıcının sohbette söylediği zamandır.
Bu adımda organize olur ve kullanıcılara kaç kişinin geldiğini bildirirsiniz. Uygulama durumuna birkaç özellik ekleyin. İlki, oturum açmış bir kullanıcının toplantıya katılıp katılmayacağını belirtme olanağıdır. İkincisi ise kaç kişinin katılacağına ilişkin bir sayaçtır.
lib/app_state.dart
dosyasında, kullanıcı arayüzü kodunun bu durumla etkileşim kurabilmesi içinApplicationState
dosyasının accessors bölümüne aşağıdaki satırları ekleyin:
lib/app_state.dart
int _attendees = 0;
int get attendees => _attendees;
Attending _attending = Attending.unknown;
StreamSubscription<DocumentSnapshot>? _attendingSubscription;
Attending get attending => _attending;
set attending(Attending attending) {
final userDoc = FirebaseFirestore.instance
.collection('attendees')
.doc(FirebaseAuth.instance.currentUser!.uid);
if (attending == Attending.yes) {
userDoc.set(<String, dynamic>{'attending': true});
} else {
userDoc.set(<String, dynamic>{'attending': false});
}
}
ApplicationState
'nininit()
yöntemini aşağıdaki şekilde güncelleyin:
lib/app_state.dart
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
FirebaseUIAuth.configureProviders([
EmailAuthProvider(),
]);
// Add from here...
FirebaseFirestore.instance
.collection('attendees')
.where('attending', isEqualTo: true)
.snapshots()
.listen((snapshot) {
_attendees = snapshot.docs.length;
notifyListeners();
});
// ...to here.
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loggedIn = true;
_emailVerified = user.emailVerified;
_guestBookSubscription = FirebaseFirestore.instance
.collection('guestbook')
.orderBy('timestamp', descending: true)
.snapshots()
.listen((snapshot) {
_guestBookMessages = [];
for (final document in snapshot.docs) {
_guestBookMessages.add(
GuestBookMessage(
name: document.data()['name'] as String,
message: document.data()['text'] as String,
),
);
}
notifyListeners();
});
// Add from here...
_attendingSubscription = FirebaseFirestore.instance
.collection('attendees')
.doc(user.uid)
.snapshots()
.listen((snapshot) {
if (snapshot.data() != null) {
if (snapshot.data()!['attending'] as bool) {
_attending = Attending.yes;
} else {
_attending = Attending.no;
}
} else {
_attending = Attending.unknown;
}
notifyListeners();
});
// ...to here.
} else {
_loggedIn = false;
_emailVerified = false;
_guestBookMessages = [];
_guestBookSubscription?.cancel();
_attendingSubscription?.cancel(); // new
}
notifyListeners();
});
}
Bu kod, katılımcı sayısını belirlemek için her zaman abone olan bir sorgu ve kullanıcının katılıp katılmadığını belirlemek için yalnızca kullanıcı giriş yapmış durumdayken etkin olan ikinci bir sorgu ekler.
lib/app_state.dart
dosyasının en üstüne aşağıdaki listelemeyi ekleyin.
lib/app_state.dart
enum Attending { yes, no, unknown }
- Yeni bir dosya (
yes_no_selection.dart
) oluşturun, radyo düğmeleri gibi çalışan yeni bir widget tanımlayın:
lib/yes_no_selection.dart
import 'package:flutter/material.dart';
import 'app_state.dart';
import 'src/widgets.dart';
class YesNoSelection extends StatelessWidget {
const YesNoSelection(
{super.key, required this.state, required this.onSelection});
final Attending state;
final void Function(Attending selection) onSelection;
@override
Widget build(BuildContext context) {
switch (state) {
case Attending.yes:
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
FilledButton(
onPressed: () => onSelection(Attending.yes),
child: const Text('YES'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () => onSelection(Attending.no),
child: const Text('NO'),
),
],
),
);
case Attending.no:
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
TextButton(
onPressed: () => onSelection(Attending.yes),
child: const Text('YES'),
),
const SizedBox(width: 8),
FilledButton(
onPressed: () => onSelection(Attending.no),
child: const Text('NO'),
),
],
),
);
default:
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
StyledButton(
onPressed: () => onSelection(Attending.yes),
child: const Text('YES'),
),
const SizedBox(width: 8),
StyledButton(
onPressed: () => onSelection(Attending.no),
child: const Text('NO'),
),
],
),
);
}
}
}
Parametre, Evet veya Hayır seçilmeden, belirsiz bir durumda başlar. Kullanıcı katılıp katılmayacağını seçtikten sonra bu seçenek doldurulmuş bir düğmeyle vurgulanmış olarak gösterilir ve diğer seçenek düz oluşturma işlemiyle birlikte kaybolur.
YesNoSelection
özelliğinden yararlanmak içinHomePage
adlı kullanıcınınbuild()
yöntemini güncelleyin, giriş yapmış bir kullanıcının katılıp katılmayacağını belirlemesini etkinleştirin ve etkinlikteki katılımcı sayısını görüntüleyin:
lib/home_page.dart
Consumer<ApplicationState>(
builder: (context, appState, _) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Add from here...
switch (appState.attendees) {
1 => const Paragraph('1 person going'),
>= 2 => Paragraph('${appState.attendees} people going'),
_ => const Paragraph('No one going'),
},
// ...to here.
if (appState.loggedIn) ...[
// Add from here...
YesNoSelection(
state: appState.attending,
onSelection: (attending) => appState.attending = attending,
),
// ...to here.
const Header('Discussion'),
GuestBook(
addMessage: (message) =>
appState.addMessageToGuestBook(message),
messages: appState.guestBookMessages,
),
],
],
),
),
Kural ekleyin
Halihazırda bazı kurallar oluşturduğunuz için düğmelerle eklediğiniz veriler reddedilir. Kuralları, attendees
koleksiyonuna ekleme yapılmasına izin verecek şekilde güncellemeniz gerekir.
attendees
koleksiyonunda, belge adı olarak kullandığınız Kimlik Doğrulama UID'sini alın ve göndereninuid
değerinin, yazdığı belgeyle aynı olduğunu doğrulayın:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ... //
match /attendees/{userId} {
allow read: if true;
allow write: if request.auth.uid == userId;
}
}
}
Bu sayede, gizli veri olmadığından herkes katılımcı listesini okuyabilir, ancak listeyi yalnızca içerik üretici güncelleyebilir.
- Beklenen tüm alanların dokümanda bulunduğundan emin olmak için veri doğrulaması ekleyin:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ... //
match /attendees/{userId} {
allow read: if true;
allow write: if request.auth.uid == userId
&& "attending" in request.resource.data;
}
}
}
- İsteğe bağlı: Uygulamada düğmeleri tıklayarak sonuçları Firebase konsolundaki Firestore kontrol panelinde görebilirsiniz.
Uygulama önizlemesi
10. Tebrikler!
Etkileşimli, gerçek zamanlı bir web uygulaması oluşturmak için Firebase'i kullandınız.