1. Başlamadan önce
Bu codelab'de Android ve iOS için Flutter mobil uygulamaları oluşturmaya yönelik Firebase'in bazı temel bilgilerini öğreneceksiniz.
Önkoşullar
- Flutter'a aşinalık
- Flutter SDK'sı
- Seçtiğiniz bir metin editörü
Ne öğreneceksin
- Flutter ile Android, iOS, Web ve macOS'ta etkinlik RSVP'si ve ziyaretçi defteri sohbet uygulaması nasıl oluşturulur?
- Firebase Authentication ile kullanıcıların kimlikleri nasıl doğrulanır ve veriler Firestore ile nasıl senkronize edilir?
İhtiyacınız olan şey
Aşağıdaki cihazlardan herhangi biri:
- Bilgisayarınıza bağlı ve geliştirici moduna ayarlanmış fiziksel bir Android veya iOS cihazı.
- iOS simülatörü ( Xcode araçları gerektirir).
- Android emülatörü ( Android Studio'da kurulum gerektirir).
Ayrıca aşağıdakilere de ihtiyacınız var:
- Google Chrome gibi seçtiğiniz bir tarayıcı.
- Android Studio veya Visual Studio Code gibi Dart ve Flutter eklentileriyle yapılandırılmış, seçtiğiniz bir IDE veya metin düzenleyici.
- Sınırda yaşamaktan hoşlanıyorsanız Flutter'ın en son
stable
sürümü veyabeta
. - Firebase projenizin oluşturulması ve yönetimi için bir Google Hesabı.
-
Firebase
CLI, Google Hesabınıza giriş yaptı.
2. Örnek kodu alın
Projenizin ilk sürümünü GitHub'dan indirin:
- Komut satırından GitHub deposunu
flutter-codelabs
dizinine kopyalayın:
git clone https://github.com/flutter/codelabs.git flutter-codelabs
flutter-codelabs
dizini, bir codelab 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. Mesela 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 atlamak veya bir adımdan sonra bir şeyin nasıl görünmesi gerektiğini görmek istiyorsanız, ilgilendiğiniz adımın adını taşıyan dizine bakın.
Başlangıç uygulamasını içe aktar
- Tercih ettiğiniz IDE'de
flutter-codelabs/firebase-get-to-know-flutter/step_02
dizinini açın veya içe aktarın. Bu dizin, henüz işlevsel olmayan bir Flutter buluşma uygulamasından oluşan codelab'in başlangıç kodunu içerir.
Çalışması gereken dosyaları bulun
Bu uygulamadaki kod birden fazla dizine yayılmıştır. Bu işlevsellik ayrımı, kodu işlevselliğe göre gruplandırdığından işi kolaylaştırır.
- Aşağıdaki 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 stilini standartlaştırmaya yardımcı olacak bir avuç widget içerir. Başlangıç uygulamasının ekranını oluştururlar. -
lib/src/authentication.dart
: Bu dosya, Firebase e-posta tabanlı kimlik doğrulama için oturum açma kullanıcı deneyimi oluşturmak amacıyla bir dizi widget içeren Kimlik Doğrulamanın kısmi bir uygulamasını içerir. Kimlik doğrulama akışına yönelik bu widget'lar henüz başlangıç uygulamasında kullanılmıyor ancak yakında ekleyeceksiniz.
-
Uygulamanın geri kalanını oluşturmak için gerektiği kadar 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'u keşfedebilir ve orada 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
açıklanan sayfa düzenindeki dağınıklığı azaltmak için yinelenen kodları ortadan kaldırır. Bu aynı zamanda tutarlı bir görünüm ve his sağlar.
Uygulamanız Android, iOS, Web ve macOS'ta şöyle görünür:
3. Firebase projesi oluşturun ve yapılandırın
Etkinlik bilgilerinin görüntülenmesi misafirleriniz için harikadır ancak tek başına hiç kimse için pek yararlı değildir. 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 girin ve ardından Devam'a tıklayın.
- Proje oluşturma seçeneklerine tıklayın. İ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 edinmek için Firebase projelerini anlama konusuna bakın.
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 anında bildirim alır.
- Firebase Güvenlik Kuralları: Veritabanınızı korur.
Bu ürünlerden bazılarının özel konfigürasyona ihtiyacı vardır veya bunları Firebase konsolunda etkinleştirmeniz gerekir.
E-postayla oturum açma kimlik doğrulamasını etkinleştir
- Firebase konsolunun Projeye genel bakış bölmesinde Oluştur menüsünü genişletin.
- Kimlik Doğrulama > Başlarken > Oturum açma yöntemi > E-posta/Parola > Etkinleştir > Kaydet öğesine tıklayın.
Firestore'u etkinleştir
Web uygulaması, sohbet mesajlarını kaydetmek ve yeni sohbet mesajları almak için Firestore'u kullanır.
Firestore'u etkinleştirin:
- Oluştur menüsünde Firestore Veritabanı > Veritabanı oluştur öğesine tıklayın.
- Test modunda başlat'ı seçin ve ardından güvenlik kurallarıyla ilgili sorumluluk reddi beyanını okuyun. Test modu, geliştirme sırasında veritabanına serbestçe yazabilmenizi sağlar.
- İleri'ye tıklayın ve ardından veritabanınızın konumunu seçin. Varsayılanı kullanabilirsiniz. Konumu daha sonra değiştiremezsiniz.
- Etkinleştir'i tıklayın.
4. Firebase'i yapılandırın
Firebase'i Flutter ile kullanmak için Flutter projesini FlutterFire
kitaplıklarını doğru kullanacak şekilde yapılandırmak üzere aşağıdaki görevleri tamamlamanız gerekir:
-
FlutterFire
bağımlılıklarını projenize ekleyin. - İstediğiniz platformu Firebase projesine kaydedin.
- Platforma özel yapılandırma dosyasını indirin ve ardından koda ekleyin.
Flutter uygulamanızın üst düzey dizininde sırasıyla iOS ve Android için platforma özel konfigürasyon dosyalarını barındıran android
, ios
, macos
ve web
alt dizinleri bulunmaktadır.
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şim 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 runner projelerini de yapılandırmanız gerekiyor. Ayrıca iş mantığının ekran mantığından ayrılmasını sağlayan provider
paketini de kullanırsınız.
FlutterFire CLI'yi yükleyin
FlutterFire CLI, temeldeki 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 kurulduktan sonra dünya çapında kullanılabilir.
Uygulamalarınızı yapılandırın
CLI, belirli bir platform için tüm yapılandırmayı oluşturmak üzere Firebase projenizden ve seçilen proje uygulamalarınızdan bilgileri çıkarı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 dayalı olarak veya Firebase Konsolundan bir Firebase projesi seçin. - Yapılandırma için Android, iOS, macOS ve web gibi platformları belirleyin.
- Yapılandırmanın çıkarılacağı 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'u yapılandırın
MacOS'ta Flutter, tamamen korumalı alana alınmış uygulamalar oluşturur. Bu uygulama, Firebase sunucularıyla iletişim kurmak için ağla entegre olduğundan, uygulamanızı ağ istemci 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 için bkz. Flutter için Masaüstü desteği .
5. RSVP işlevini ekleyin
Artık Firebase'i uygulamaya eklediğinize göre, kişileri Kimlik Doğrulama ile kaydeden bir RSVP düğmesi oluşturabilirsiniz. Android yerel, iOS yerel ve Web için önceden oluşturulmuş FirebaseUI Auth
paketleri vardır, ancak bu özelliği Flutter için oluşturmanız gerekir.
Daha önce aldığınız proje, kimlik doğrulama akışının çoğu 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ığı ekleyin
Uygulamanın Flutter widget'ları ağacında merkezi bir uygulama durumu nesnesinin kullanılabilir olmasını sağlamak 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ında kullanılabilir hale getiren provider
paketini çeker ve firebase_ui_auth
paketindeki kimlik doğrulama widget'larını içerir.
Bu ApplicationState
uygulama durumu nesnesinin bu adım için bir ana sorumluluğu vardır; bu, widget ağacını, kimliği doğrulanmış bir duruma yönelik bir güncelleme olduğu konusunda uyarmaktır.
Bir sağlayıcıyı yalnızca kullanıcının oturum açma durumunun durumunu uygulamaya iletmek için kullanırsınız. Bir kullanıcının oturum açmasına izin vermek için firebase_ui_auth
paketi tarafından sağlanan kullanıcı arayüzlerini kullanırsınız; bu, uygulamalarınızdaki oturum açma ekranlarını hızlı bir şekilde önyüklemenin harika bir yoludur.
Kimlik doğrulama akışını entegre edin
- İçe aktarma işlemlerini
lib/main.dart
dosyasının üst kısmında 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
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, sağlayıcı paketini ChangeNotifierProvider
widget'ıyla uygulama durumu nesnesinin başlatılmasından sorumlu kılar. Bu özel provider
sınıfını kullanırsınız çünkü uygulama durumu nesnesi, provider
paketinin bağımlı pencere öğelerini ne zaman yeniden görüntüleyeceğini bilmesini sağlayan ChangeNotifier
sınıfını genişletir.
- Bir
GoRouter
yapılandırması oluşturarak, FirebaseUI'nin size sağladığı farklı ekranlarda gezinmeyi yönetecek şekilde uygulamanızı 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.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, kimlik doğrulama akışının yeni durumuna bağlı olarak kendisiyle ilişkilendirilen farklı türde bir eyleme sahiptir. Kimlik doğrulamadaki çoğu durum değişikliğinden sonra, ister ana ekran ister profil gibi farklı bir ekran olsun, tercih ettiğiniz bir ekrana yeniden yönlendirebilirsiniz.
-
HomePage
sınıfının derleme yönteminde, uygulama durumunuAuthFunc
widget'ıyla tümleştirin:
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ı başlatır ve onu bir Consumer
widget'ına sararsınız. Tüketici widget'ı, uygulama durumu değiştiğinde provider
paketinin ağacın bir kısmını yeniden oluşturmak için kullanılabileceği olağan yoldur. AuthFunc
widget'ı, test ettiğiniz tamamlayıcı widget'lardır.
Kimlik doğrulama akışını test edin
- Uygulamada
SignInScreen
başlatmak için RSVP düğmesine dokunun.
- Bir e-mail adresi girin. Zaten kayıtlıysanız sistem sizden bir şifre girmenizi ister. Aksi taktirde sistem sizden kayıt formunu doldurmanızı isteyecektir.
- Hata işleme akışını kontrol etmek için altı karakterden kısa bir şifre girin. Kayıtlıysanız bunun yerine şifreyi görürsünüz.
- Hata işleme akışını kontrol etmek için yanlış şifreler girin.
- Doğru şifreyi girin. Kullanıcıya oturumu kapatma olanağı sunan oturum açma deneyimini görürsünüz.
6. Firestore'a mesaj yazın
Kullanıcıların geldiğini bilmek harika, ancak konuklara uygulamada yapacak başka bir şey vermeniz gerekiyor. Ya ziyaretçi defterine mesaj bırakabilselerdi? Gelmekten neden heyecan duyduklarını veya kiminle tanışmayı umduklarını paylaşabilirler.
Kullanıcıların uygulamada yazdığı sohbet mesajlarını depolamak için Firestore'u kullanırsınız.
Veri örneği
Firestore bir NoSQL veritabanıdır ve veritabanında depolanan veriler koleksiyonlara, belgelere, alanlara ve alt koleksiyonlara bölünmüştür. Sohbetin her mesajını, üst düzey bir koleksiyon olan guestbook
koleksiyonunda bir belge olarak saklarsınız.
Firestore'a mesaj ekleme
Bu bölümde, kullanıcıların veritabanına mesaj yazmasına yönelik işlevselliği eklersiniz. Öncelikle form alanı ve gönder butonunu ekliyorsunuz, ardından bu elemanları veritabanına bağlayan kodu ekliyorsunuz.
-
guest_book.dart
adında yeni bir dosya oluşturun, bir mesaj alanının kullanıcı arayüzü öğelerini ve bir gönder düğmesini oluşturmak için birGuestBook
durum bilgisi widget'ı ekleyin:
lib/misafir_kitabı.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ç ilgi çekici nokta var. İlk olarak, mesajın gerçekten içerik içerdiğini doğrulayabilmeniz ve eğer içerik yoksa kullanıcıya bir hata mesajı gösterebilmeniz için bir form oluşturursunuz. 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 bkz. Anahtarlar Ne Zaman Kullanılır ?
Ayrıca widget'ların düzenlenme şekline de dikkat edin; TextFormField
içeren bir Row
ve Row
içeren bir StyledButton
var. Ayrıca TextFormField
öğesinin, TextFormField
satırdaki fazladan boşluğu doldurmaya zorlayan Expanded
bir widget'a sarıldığını da unutmayın. Bunun neden gerekli olduğunu daha iyi anlamak için bkz. Kısıtlamaları anlama.
Artık kullanıcının Ziyaretçi Defterine eklemek üzere metin girmesine olanak tanıyan bir widget'ınız olduğuna göre, onu ekrana getirmeniz gerekir.
-
ListView
alt öğelerinin sonuna aşağıdaki iki satırı eklemek içinHomePage
gövdesini düzenleyin:
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. Bu kodu işlevsel hale getirmek için kısa süre içinde güncellersiniz.
Uygulama önizlemesi
Bir kullanıcı GÖNDER'i tıkladığında aşağıdaki kod parçacığı tetiklenir. Mesaj giriş alanının içeriğini veritabanının guestbook
koleksiyonuna ekler. Özellikle, addMessageToGuestBook
yöntemi, mesaj içeriğini guestbook
koleksiyonunda otomatik olarak oluşturulan bir kimlikle yeni bir belgeye ekler.
FirebaseAuth.instance.currentUser.uid
öğesinin, Kimlik Doğrulamanın oturum açmış tüm kullanıcılar için sağladığı otomatik olarak oluşturulan benzersiz kimliğe bir referans olduğunu unutmayın.
-
lib/app_state.dart
dosyasınaaddMessageToGuestBook
yöntemini ekleyin. Bir sonraki adımda bu yeteneği kullanıcı arayüzüne bağlarsı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ğlayın
Kullanıcının Ziyaretçi Defterine eklemek istediği metni girebileceği bir kullanıcı arayüzünüz var ve girişi Firestore'a eklemek için kodunuz var. Şimdi tek yapmanız gereken ikisini birbirine bağlamak.
-
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. Uygulama durumunu ağacın oluşturduğunuz kısmında kullanılabilir hale getirmek için yine Consumer<ApplicationState>
öğesini kullanırsınız. Bu, kullanıcı arayüzüne mesaj giren birine tepki vermenizi ve bunu veritabanında yayınlamanızı sağlar. Bir sonraki bölümde eklenen mesajların veritabanında yayınlanıp yayınlanmadığını test edeceksiniz.
Mesaj göndermeyi test edin
- Gerekirse uygulamada oturum açın.
-
Hey there!
gibi bir mesaj girin. ve ardından GÖNDER'i tıklayın.
Bu eylem, mesajı Firestore veritabanınıza yazar. Ancak, gerçek Flutter uygulamanızda mesajı göremezsiniz çünkü bir sonraki adımda yapacağınız veri alımını gerçekleştirmeniz gerekir. Ancak Firebase konsolunun Veritabanı kontrol panelinde , eklenen mesajınızı guestbook
koleksiyonunda görebilirsiniz. Daha fazla mesaj gönderirseniz guestbook
koleksiyonunuza daha fazla belge eklersiniz. Örneğin aşağıdaki kod parçacığına bakın:
7. Mesajları okuyun
Konukların veritabanına mesaj yazabilmesi ama bunları henüz uygulamada görememesi çok güzel. Bunu düzeltmenin zamanı geldi!
Mesajları senkronize et
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. Uygulamadan yeni eklenen mesajları dinleyen uygulama durumuna kod eklersiniz.
- Yeni bir
guest_book_message.dart
dosyası oluşturun ve Firestore'da depoladığınız verilerin yapılandırılmış bir 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 aktarmaları 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
-
ApplicationState
durumu ve alıcıları tanımladığınız bölümüne 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
başlatma bölümünde, kullanıcı oturum açtığında belge 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 önemlidir çünkü burası guestbook
koleksiyonu üzerinde bir sorgu oluşturacağınız ve bu koleksiyona abone olma ve abonelikten çıkma işlemlerini gerçekleştireceğiniz yerdir. guestbook
koleksiyonundaki mesajların yerel önbelleğini yeniden oluşturduğunuz akışı dinlersiniz ve ayrıca daha sonra aboneliğinizi iptal edebilmek için bu aboneliğe bir referans saklarsınız. Burada çok şey oluyor, bu yüzden daha net bir zihinsel model elde etmek için ne olduğunu incelemek için bunu bir hata ayıklayıcıda keşfetmelisiniz. Daha fazla bilgi için bkz. Firestore ile gerçek zamanlı güncellemeler alma .
-
lib/guest_book.dart
dosyasına aşağıdaki içe aktarmayı ekleyin:
import 'guest_book_message.dart';
-
GuestBook
widget'ına, bu değişen durumu kullanıcı arayüzüne bağlamak için yapılandırmanın bir parçası olarak bir mesaj listesi ekleyin:
lib/misafir_kitabı.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
, bu yapılandırmayı ortaya çıkarmak içinbuild
yöntemini aşağıdaki gibi değiştirin:
lib/misafir_kitabı.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 bir Column
widget'ıyla sararsınız ve ardından mesaj listesindeki her mesaj için yeni bir Paragraph
oluşturmak üzere Column
alt öğelerinin kuyruğuna bir koleksiyon eklersiniz.
-
GuestBook
yenimessages
parametresiyle doğru şekilde oluşturmak içinHomePage
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
),
],
],
),
),
Test mesajı senkronizasyonu
Firestore, verileri veritabanına abone olan istemcilerle otomatik ve anında senkronize eder.
Test mesajı senkronizasyonu:
- Uygulamada, daha önce veritabanında oluşturduğunuz mesajları bulun.
- Yeni mesajlar yazın. Anında görünürler.
- Çalışma alanınızı birden çok 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 mesajları manuel olarak silin, değiştirin veya yeni mesajlar ekleyin. Tüm değişiklikler kullanıcı arayüzünde görünür.
Tebrikler! Firestore belgelerini uygulamanızda okuyorsunuz!
Uygulama önizlemesi
8. Temel güvenlik kurallarını ayarlayın
Başlangıçta Firestore'u test modunu kullanacak şekilde ayarladınız; bu, veritabanınızın okuma ve yazma işlemlerine 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ı ayarlamanız gerekir. Güvenlik, uygulamanızın yapısının ve davranışının ayrılmaz bir parçasıdır.
Firebase Güvenlik Kuralları, veritabanınızdaki belgelere ve koleksiyonlara erişimi kontrol etmenize olanak tanır. Esnek kural sözdizimi, veritabanının tamamına yapılan tüm yazma işlemlerinden belirli bir belgedeki işlemlere kadar her şeyi eşleştiren kurallar oluşturmanıza olanak tanır.
Temel güvenlik kurallarını ayarlayın:
- Firebase konsolunun Geliştir menüsünde Veritabanı > Kurallar'ı tıklayın. Aşağıdaki varsayılan güvenlik kurallarını ve kuralların herkese açık olduğuna ilişkin bir uyarı görmelisiniz:
- Uygulamanın veri yazdığı koleksiyonları tanımlayın:
match /databases/{database}/documents
dosyasında, güvenliğini sağlamak istediğiniz koleksiyonu tanımlayın:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /guestbook/{entry} {
// You'll add rules here in the next step.
}
}
Kimlik Doğrulama UID'sini her ziyaretçi defteri belgesinde bir alan olarak kullandığınız için, Kimlik Doğrulama UID'sini alabilir ve belgeye yazmaya çalışan herkesin eşleşen bir Kimlik Doğrulama UID'sine sahip olduğunu doğrulayabilirsiniz.
- Okuma ve yazma kurallarını kural kümenize 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 ziyaretçi defterindeki mesajları yalnızca oturum açmış kullanıcılar okuyabilir, ancak yalnızca mesajın yazarı bir mesajı düzenleyebilir.
- Beklenen tüm alanların belgede mevcut olduğundan emin olmak için veri doğrulama 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
Bir katılımcının LCV durumunu kaydedin
Şu anda uygulamanız insanların yalnızca etkinlikle ilgilendiklerinde sohbet etmesine izin veriyor. Ayrıca birisinin gelip gelmediğini bilmenin tek yolu sohbette bunu söylemesidir.
Bu adımda organize olursunuz ve kaç kişinin geleceğini insanlara bildirirsiniz. Uygulama durumuna birkaç özellik eklersiniz. Bunlardan ilki, oturum açmış bir kullanıcının katılıp katılmayacağını belirleme yeteneğidir. İkincisi ise kaç kişinin katıldığını gösteren bir sayaçtır.
- UI kodunun bu durumla etkileşim kurabilmesi için
lib/app_state.dart
dosyasındaApplicationState
erişimciler 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
init()
yöntemini aşağıdaki gibi 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 olunan bir sorgu ve kullanıcının katılıp katılmadığını belirlemek için yalnızca kullanıcı oturum açtığında etkin olan ikinci bir sorgu ekler.
- Aşağıdaki numaralandırmayı
lib/app_state.dart
dosyasının en üstüne ekleyin.
lib/app_state.dart
enum Attending { yes, no, unknown }
- Yeni bir
yes_no_selection.dart
dosyası oluşturun, radyo düğmeleri gibi davranan 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'),
),
],
),
);
}
}
}
Ne Evet ne de Hayır seçiliyken belirsiz bir durumda başlar. Kullanıcı katılıp katılmayacağını seçtiğinde, bu seçeneği dolu bir düğmeyle vurgulanmış olarak gösterirsiniz ve diğer seçenek düz bir görüntüyle geride kalır.
-
YesNoSelection
avantajından yararlanmak içinHomePage
build()
yöntemini güncelleyin, oturum açmış bir kullanıcının katılıp katılmayacağını belirlemesini sağlayın ve etkinliğe katılanların 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 ekle
Zaten bazı kurallar ayarladınız, bu nedenle düğmelerle eklediğiniz veriler reddedilecek. attendees
koleksiyonuna ekleme yapılmasına izin vermek için kuralları güncellemeniz gerekir.
-
attendees
koleksiyonunda, belge adı olarak kullandığınız Kimlik Doğrulama UID'sini alın ve göndereninuid
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, orada özel veri olmadığından herkesin katılımcı listesini okumasına olanak tanır, ancak listeyi yalnızca oluşturan kişi güncelleyebilir.
- Beklenen tüm alanların belgede mevcut olduğundan emin olmak için veri doğrulama 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, Firebase konsolundaki Firestore kontrol panelindeki sonuçları görmek için düğmeleri tıklayın.
Uygulama önizlemesi
10. Tebrikler!
Etkileşimli, gerçek zamanlı bir web uygulaması oluşturmak için Firebase'i kullandınız!