Flutter için Firebase'i tanıyın

1. Başlamadan önce

Bu codelab'de, Android ve iOS için Flutter mobil uygulamaları oluşturmak üzere Firebase'in temel özelliklerinden bazılarını öğreneceksiniz.

Ön koşullar

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.

Android'de uygulamanın ana ekranı

iOS'teki uygulamanın ana ekranı

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ı gerekir).
  • Android emülatörü (Android Studio'da kurulum gerekir).

Ayrıca aşağıdakilere de ihtiyacınız vardır:

  • Google Chrome gibi tercih ettiğiniz bir tarayıcı.
  • Dart ve Flutter eklentileriyle yapılandırılmış bir IDE veya metin düzenleyici (ör. Android Studio veya Visual Studio Code).
  • Flutter'ın en son stable sürümü veya yenilikleri takip ediyorsanız beta.
  • 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

Projenizin ilk sürümünü GitHub'dan indirin:

  1. 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.

  1. İ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üneceğini görmek istiyorsanız ilgilendiğiniz adımın adını taşıyan dizine bakın.

Başlangıç uygulamasını içe aktarma

  • 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 codelab'in 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.

  • 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 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 içeren Authentication'in kısmi bir uygulamasını içerir. Kimlik doğrulama akışı için bu widget'lar henüz başlangıç uygulamasında kullanılmamaktadır ancak yakında eklenecektir.

Uygulamanın geri kalanını oluşturmak için gerektiğinde 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ında bulunan 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, tutarlı bir görünüm ve tarz elde etmenizi de sağlar.

Uygulamanızın Android, iOS, web ve macOS'te nasıl göründüğü aşağıda açıklanmıştır:

Android'de uygulamanın ana ekranı

iOS'teki uygulamanın ana ekranı

Uygulamanın web'deki ana ekranı

macOS'te uygulamanın ana ekranı

3. Firebase projesi oluşturup yapılandırma

Etkinlik bilgilerinin gösterilmesi konuklarınız için mükemmel olsa da tek başına pek faydalı değildir. Uygulamaya dinamik işlevler eklemeniz gerekir. Bunun 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

  1. Firebase'de oturum açın.
  2. Konsolda Proje ekle veya Proje oluştur'u tıklayın.
  3. Proje adı alanına Firebase-Flutter-Codelab yazın ve Devam'ı tıklayın.

4395e4e67c08043a.png

  1. Proje oluşturma seçeneklerini tıklayın. İstenirse Firebase şartlarını kabul edin ancak bu uygulama için kullanmayacağınız için Google Analytics kurulumunu atlayın.

b7138cde5f2c7b61.png

Firebase projeleri hakkında daha fazla bilgi edinmek 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 bulutta saklar ve veriler değiştiğinde anında bildirim alır.
  • Firebase Güvenlik Kuralları: Veritabanınıza güvenlik 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

  1. Firebase konsolunun Projeye genel bakış bölmesinde Derleme menüsünü genişletin.
  2. Kimlik doğrulama > Başlayın > Oturum açma yöntemi > E-posta/Şifre > Etkinleştir > Kaydet'i tıklayın.

58e3e3e23c2f16a4.png

Firestore'u ayarlama

Web uygulaması, sohbet mesajlarını kaydetmek ve yeni sohbet mesajları almak için Firestore'u kullanır.

Firestore'u Firebase projenizde ayarlamak için:

  1. Firebase konsolunun sol panelinde Derleme'yi genişletin ve ardından Firestore veritabanı'nı seçin.
  2. Create database'i (Veritabanı oluştur) tıklayın.
  3. Veritabanı Kimliği'ni (default) olarak bırakın.
  4. Veritabanı için bir konum seçip Sonraki'yi tıklayın.
    Gerçek bir uygulama için kullanıcılarınıza yakın bir konum seçmeniz gerekir.
  5. 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.
  6. 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:

  1. Projenize FlutterFire bağımlılıklarını ekleyin.
  2. İstediğiniz platformu Firebase projesine kaydedin.
  3. Platforma özgü yapılandırma dosyasını indirip koda ekleyin.

Flutter uygulamanızın üst düzey dizininde, sırasıyla iOS ve Android için platforma özgü 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ını sağlayan provider paketini de kullanırsınız.

FlutterFire CLI'yi yükleme

FlutterFire CLI, temel Firebase CLI'ye bağlıdır.

  1. Henüz yapmadıysanız makinenize Firebase CLI'yi yükleyin.
  2. FlutterFire CLI'yi yükleyin:
$ dart pub global activate flutterfire_cli

Yüklendikten sonra flutterfire komutu dünya genelinde 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 süreçlerde size rehberlik eder:

  1. .firebaserc dosyasına göre veya Firebase Konsolu'ndan bir Firebase projesi seçin.
  2. Android, iOS, macOS ve web gibi yapılandırma platformlarını belirleyin.
  3. 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.
  4. Projenizde bir firebase_options.dart dosyası oluşturun.

macOS'i yapılandırma

macOS'te Flutter, tamamen korumalı alan içinde uygulamalar oluşturur. Bu uygulama, Firebase sunucularıyla iletişim kurmak için ağ ile 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 Flutter için masaüstü desteği başlıklı makaleyi inceleyin.

5. LCV işlevi ekleme

Firebase'i uygulamaya eklediğinize göre, kullanıcıları Authentication ile kaydeden bir LCV düğmesi oluşturabilirsiniz. Android yerel, iOS yerel ve web için önceden oluşturulmuş FirebaseUI Auth paketleri vardır ancak Flutter için bu özelliği kendiniz 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ığı ekleme

Merkezi bir uygulama durumu nesnesini uygulamanın Flutter widget'ı ağacında kullanılabilir hale getirmek için provider paketini kullanın:

  1. 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

  1. 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';
  1. 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'ıyla oluşturulmasından sağlayıcı paketini sorumlu kılar. Uygulama durumu nesnesi ChangeNotifier sınıfını genişlettiği için bu belirli provider sınıfını kullanırsınız. Bu, provider paketinin bağımlı widget'ları ne zaman yeniden göstereceğini bilmesini sağlar.

  1. 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 ilişkili farklı bir işlem türü vardır. Kimlik doğrulamadaki çoğu durum değişikliğinden sonra, ana ekran veya profil gibi tercih ettiğiniz bir ekrana geri yönlendirebilirsiniz.

  1. HomePage sınıfının build yönteminde uygulama durumunu AuthFunc 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ı örneklendirip bir Consumer widget'ına sararsı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

cdf2d25e436bd48d.png

  1. Uygulamada SignInScreen'yi başlatmak için LCV düğmesine dokunun.

2a2cd6d69d172369.png

  1. E-posta adresi girin. Zaten kayıtlıysanız sistem sizden bir şifre girmenizi ister. Aksi takdirde sistem, kayıt formunu doldurmanızı ister.

e5e65065dba36b54.png

  1. Hata işleme akışını kontrol etmek için altı karakterden kısa bir şifre girin. Kaydolduysanız bunun yerine şifreyi görürsünüz.
  2. Hata işleme akışını kontrol etmek için yanlış şifreler girin.
  3. Doğru şifreyi girin. Kullanıcıya oturumu kapatma olanağı sunan oturum açmış kullanıcı deneyimini görürsünüz.

4ed811a25b0cf816.png

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

NoSQL veritabanı olan Firestore'da depolanan veriler koleksiyonlar, belgeler, alanlar ve alt koleksiyonlara ayrılır. Sohbetin her mesajını üst düzey bir koleksiyon olan guestbook koleksiyonunda doküman olarak depolarsınız.

7c20dc8424bb1d84.png

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 eklersiniz.

  1. 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 bir GuestBook 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 nokta var. Öncelikle, iletilerin gerçekten içerik içerdiğini doğrulayabilmek ve içerik yoksa kullanıcıya bir hata mesajı göstermek için bir form oluşturursunuz. Bir formu doğrulamak için GlobalKey ile formun arkasındaki form durumuna 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ı ve bu durumun TextFormField öğesini satırdaki fazladan alanı doldurmaya zorladığını unutmayın. Bunun neden gerekli olduğunu daha iyi anlamak için Kısıtlamaları anlama başlıklı makaleyi inceleyin.

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.

  1. HomePage öğesinin gövdesini düzenleyerek ListView öğ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. Bu kodu, çalışır hale getirmek için kısa süre içinde güncelleyebilirsiniz.

Uygulama önizlemesi

Android&#39;de sohbet entegrasyonuna sahip uygulamanın ana ekranı

iOS&#39;teki uygulamanın sohbet entegrasyonlu ana ekranı

Sohbet entegrasyonuna sahip web&#39;deki uygulamanın ana ekranı

macOS&#39;te sohbet entegrasyonuna sahip uygulamanın ana ekranı

Kullanıcı GÖNDER'i tıkladığında aşağıdaki kod snippet'i tetiklenir. İleti giriş alanının içeriğini veritabanının guestbook koleksiyonuna ekler. Daha açık belirtmek gerekirse addMessageToGuestBook yöntemi, ileti içeriğini guestbook koleksiyonunda otomatik olarak oluşturulan bir kimliğe sahip yeni bir belgeye ekler.

FirebaseAuth.instance.currentUser.uid değerinin, kimlik doğrulamanın oturum açmış tüm kullanıcılar için verdiği otomatik olarak oluşturulan benzersiz kimliğe referans olduğunu unutmayın.

  • lib/app_state.dart dosyasına addMessageToGuestBook yöntemini ekleyin. Bu özelliği bir sonraki adımda 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ğlama

Kullanıcının, ziyaretçi defterine eklemek istediği metni girebileceği bir kullanıcı arayüzünüz ve girişi Firestore'a ekleyeceğiniz kodunuz var. Artık tek yapmanız gereken ikisini birbirine bağlamaktır.

  • lib/home_page.dart dosyasında, HomePage 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 bölümüne sunmak için tekrar Consumer<ApplicationState> kullanırsınız. Bu sayede, kullanıcı arayüzüne mesaj giren kullanıcılara tepki verebilir ve 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

  1. Gerekirse uygulamada oturum açın.
  2. Hey there! gibi bir mesaj girin ve GÖNDER'i tıklayın.

Bu işlem, iletiyi 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:

713870af0b3b63c.png

7. Mesajları okuma

Davetlilerin veritabanına mesaj yazabilmesi güzel bir özellik ancak henüz uygulamada bu mesajları göremiyorlar. Bu sorunu düzeltme zamanı.

Mesajları 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.

  1. Yeni bir dosya guest_book_message.dart oluşturun, Firestore'da depoladığınız verilerin yapılandırılmış bir görünümünü göstermek 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;
}
  1. 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
  1. 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.
  1. 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ışa dinlediğinizde guestbook koleksiyonundaki iletilerin yerel önbelleğini yeniden oluşturur ve daha sonra aboneliğinizi iptal edebilmek için bu aboneliğe ait bir referans depolarsı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.

  1. lib/guest_book.dart dosyasına aşağıdaki içe aktarma işlemini ekleyin:
import 'guest_book_message.dart';
  1. Değişen bu durumu kullanıcı arayüzüne bağlamak için GuestBook widget'ına, yapılandırmanın bir 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();
}
  1. _GuestBookState içinde, bu yapılandırmayı göstermek için build 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.
    );
  }
}

İleti listesindeki her ileti için yeni bir Paragraph oluşturmak üzere build() yönteminin önceki içeriğini bir Column widget'ı ile sarmalayın ve ardından Column öğesinin alt kısmına collection for ekleyin.

  1. Yeni messages parametresiyle GuestBook'ü doğru şekilde oluşturmak için HomePage öğ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:

  1. Uygulamada, daha önce veritabanında oluşturduğunuz iletileri bulun.
  2. Yeni mesajlar yazabilirsiniz. Bu reklamlar anında gösterilir.
  3. Çalışma alanınızı birden fazla pencerede veya sekmede açın. Mesajlar, pencereler ve sekmeler arasında gerçek zamanlı olarak senkronize edilir.
  4. İ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

Android&#39;de sohbet entegrasyonuna sahip uygulamanın ana ekranı

iOS&#39;teki uygulamanın sohbet entegrasyonlu ana ekranı

Sohbet entegrasyonuna sahip web&#39;deki uygulamanın ana ekranı

macOS&#39;te sohbet entegrasyonuna sahip uygulamanın ana ekranı

8. Temel güvenlik kuralları oluşturma

Firestore'u ilk olarak test modunu kullanacak şekilde ayarladığınız için veritabanınız okuma ve yazma işlemlerine açıktır. 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 yapılan tüm yazma işlemlerinden belirli bir belgedeki işlemlere kadar her şeyle eşleşen kurallar oluşturmanıza olanak tanır.

Temel güvenlik kuralları oluşturun:

  1. Firebase Console'un 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 olmasıyla ilgili bir uyarı görürsünüz:

7767a2d2e64e7275.png

  1. 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.

  1. 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.

  1. 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 /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

Şu anda uygulamanız yalnızca etkinlikle ilgilenen kullanıcıların sohbet etmesine izin veriyor. Ayrıca, bir kullanıcının toplantıya katılıp katılmayacağını yalnızca sohbette belirtmesi durumunda öğrenebilirsiniz.

Bu adımda, hazırlıklarınızı yapar ve kaç kişinin katılacağını katılımcılara bildirirsiniz. Uygulama durumuna birkaç özellik ekleyin. İlki, oturum açmış bir kullanıcının toplantıya katılıp katılmayacağını belirtmesidir. İkincisi, etkinliğe kaç kişinin katıldığını gösteren bir sayaçtır.

  1. Kullanıcı arayüzü kodunun bu durumla etkileşim kurabilmesi için lib/app_state.dart dosyasında, ApplicationState'ı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});
  }
}
  1. ApplicationState'nin init() 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 toplantıya katılıp katılmadığını belirlemek için yalnızca kullanıcı oturum açtığında etkin olan ikinci bir sorgu ekler.

  1. lib/app_state.dart dosyasının en üstüne aşağıdaki listelemeyi ekleyin.

lib/app_state.dart

enum Attending { yes, no, unknown }
  1. 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'),
              ),
            ],
          ),
        );
    }
  }
}

Evet veya Hayır seçilmemişken belirsiz bir durumda başlar. Kullanıcı toplantıya katılıp katılmayacağını seçtikten sonra, bu seçeneği doldurulmuş bir düğmeyle vurgulayarak gösterirsiniz. Diğer seçenek ise düz bir şekilde gösterilir.

  1. HomePage'nin build() yöntemini YesNoSelection'den yararlanacak şekilde güncelleyin, oturum açmış bir kullanıcının etkinliğe katılıp katılmayacağını belirtmesini sağlayın ve etkinliğin katılımcı sayısını gösterin:

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.

  1. attendees koleksiyonunda, belge adı olarak kullandığınız Kimlik Doğrulama UID'sini alın ve gönderenin uid değerinin, yazmakta olduğu 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 durumda, gizli veri olmadığı için herkes katılımcı listesini okuyabilir ancak yalnızca içerik üretici listeyi güncelleyebilir.

  1. 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;

    }
  }
}
  1. İsteğe bağlı: Uygulamada düğmeleri tıklayarak sonuçları Firebase konsolundaki Firestore kontrol panelinde görebilirsiniz.

Uygulama önizlemesi

Android&#39;de uygulamanın ana ekranı

iOS&#39;teki uygulamanın ana ekranı

Uygulamanın web&#39;deki ana ekranı

macOS&#39;te uygulamanın ana ekranı

10. Tebrikler!

Etkileşimli, gerçek zamanlı bir web uygulaması oluşturmak için Firebase'i kullandınız.

Daha fazla bilgi