1. قبل از شروع
در این کد لبه، برخی از اصول اولیه Firebase را برای ایجاد برنامه های موبایل Flutter برای اندروید و iOS یاد می گیرید.
پیش نیازها
- آشنایی با فلاتر
- فلاتر SDK
- یک ویرایشگر متن به انتخاب شما
چیزی که یاد خواهید گرفت
- نحوه ساختن یک رویداد RSVP و برنامه چت کتاب مهمان در Android، iOS، وب و macOS با Flutter.
- نحوه احراز هویت کاربران با Firebase Authentication و همگام سازی داده ها با Firestore.
آنچه شما نیاز دارید
هر یک از دستگاه های زیر:
- یک دستگاه فیزیکی Android یا iOS که به رایانه شما متصل شده و روی حالت توسعه دهنده تنظیم شده است.
- شبیه ساز iOS (نیاز به ابزار Xcode است).
- شبیه ساز اندروید (نیاز به راه اندازی در Android Studio دارد).
شما همچنین به موارد زیر نیاز دارید:
- مرورگر دلخواه شما، مانند گوگل کروم.
- یک IDE یا ویرایشگر متن به انتخاب شما که با افزونه های Dart و Flutter مانند Android Studio یا Visual Studio Code پیکربندی شده است.
- آخرین نسخه
stable
Flutter یاbeta
اگر از زندگی در لبه لذت می برید. - یک حساب Google برای ایجاد و مدیریت پروژه Firebase شما.
-
Firebase
CLI به حساب Google شما وارد شده است.
2. کد نمونه را دریافت کنید
نسخه اولیه پروژه خود را از GitHub دانلود کنید:
- از خط فرمان، مخزن GitHub را در فهرست
flutter-codelabs
کلون کنید:
git clone https://github.com/flutter/codelabs.git flutter-codelabs
دایرکتوری flutter-codelabs
حاوی کد مجموعه ای از کدلب ها است. کد این کد لبه در دایرکتوری flutter-codelabs/firebase-get-to-know-flutter
قرار دارد. فهرست شامل مجموعهای از عکسهای فوری است که نشان میدهد پروژه شما در پایان هر مرحله چگونه باید به نظر برسد. به عنوان مثال، شما در مرحله دوم هستید.
- فایل های منطبق را برای مرحله دوم پیدا کنید:
cd flutter-codelabs/firebase-get-to-know-flutter/step_02
اگر میخواهید به جلو بپرید یا ببینید چیزی باید بعد از یک مرحله چگونه باشد، به فهرستی که نام آن مرحله مورد علاقه شما است نگاه کنید.
برنامه شروع را وارد کنید
- دایرکتوری
flutter-codelabs/firebase-get-to-know-flutter/step_02
در IDE دلخواه خود باز کنید یا وارد کنید. این دایرکتوری حاوی کد شروع برای codelab است که از یک برنامه Flutter Meetup هنوز کاربردی نیست.
فایل هایی که نیاز به کار دارند را پیدا کنید
کد موجود در این برنامه در چندین دایرکتوری پخش شده است. این تقسیم عملکرد کار را آسان تر می کند زیرا کدها را بر اساس عملکرد گروه بندی می کند.
- فایل های زیر را بیابید:
-
lib/main.dart
: این فایل حاوی نقطه ورودی اصلی و ویجت برنامه است. -
lib/home_page.dart
: این فایل حاوی ویجت صفحه اصلی است. -
lib/src/widgets.dart
: این فایل حاوی تعدادی ویجت است که به استانداردسازی سبک برنامه کمک می کند. آنها صفحه برنامه شروع را می سازند. -
lib/src/authentication.dart
: این فایل شامل اجرای بخشی از Authentication با مجموعه ای از ویجت ها برای ایجاد یک تجربه کاربری برای ورود به سیستم برای احراز هویت مبتنی بر ایمیل Firebase است. این ویجتها برای جریان احراز هویت هنوز در برنامه شروع استفاده نمیشوند، اما به زودی آنها را اضافه میکنید.
-
برای ساخت بقیه برنامه، فایلهای اضافی را در صورت نیاز اضافه میکنید.
فایل lib/main.dart
را مرور کنید
این برنامه از بسته google_fonts
استفاده می کند تا Roboto را به عنوان فونت پیش فرض در سراسر برنامه تبدیل کند. می توانید fonts.google.com را کاوش کنید و از فونت هایی که در آنجا کشف می کنید در قسمت های مختلف برنامه استفاده کنید.
شما از ویجت های کمکی از فایل lib/src/widgets.dart
به شکل Header
، Paragraph
و IconAndDetail
استفاده می کنید. این ویجتها کدهای تکراری را حذف میکنند تا به هم ریختگی در طرحبندی صفحه که در HomePage
توضیح داده شده کاهش یابد. این همچنین یک ظاهر و احساس ثابت را امکان پذیر می کند.
در اینجا برنامه شما در Android، iOS، وب و macOS به نظر می رسد:
3. یک پروژه Firebase ایجاد و پیکربندی کنید
نمایش اطلاعات رویداد برای مهمانان شما عالی است، اما به تنهایی برای کسی مفید نیست. شما باید برخی از عملکردهای پویا را به برنامه اضافه کنید. برای انجام این کار، باید Firebase را به برنامه خود متصل کنید. برای شروع کار با Firebase، باید یک پروژه Firebase ایجاد و پیکربندی کنید.
یک پروژه Firebase ایجاد کنید
- وارد Firebase شوید.
- در کنسول، روی افزودن پروژه یا ایجاد پروژه کلیک کنید.
- در قسمت Project name ، Firebase-Flutter-Codelab را وارد کنید و سپس روی Continue کلیک کنید.
- روی گزینه های ایجاد پروژه کلیک کنید. اگر از شما خواسته شد، شرایط Firebase را بپذیرید، اما از تنظیم Google Analytics صرفنظر کنید زیرا از آن برای این برنامه استفاده نخواهید کرد.
برای کسب اطلاعات بیشتر درباره پروژههای Firebase، به درک پروژههای Firebase مراجعه کنید.
این برنامه از محصولات Firebase زیر استفاده می کند که برای برنامه های وب در دسترس هستند:
- احراز هویت: به کاربران اجازه می دهد وارد برنامه شما شوند.
- Firestore: داده های ساخت یافته را در فضای ابری ذخیره می کند و در صورت تغییر داده ها، اعلان های فوری دریافت می کند.
- قوانین امنیتی Firebase: پایگاه داده شما را ایمن می کند.
برخی از این محصولات نیاز به پیکربندی خاصی دارند یا باید آنها را در کنسول Firebase فعال کنید.
احراز هویت ورود به ایمیل را فعال کنید
- در پنجره نمای کلی پروژه کنسول Firebase، منوی ساخت را گسترش دهید.
- روی تأیید هویت > شروع به کار > روش ورود به سیستم > ایمیل/گذرواژه > فعال کردن > ذخیره کلیک کنید.
Firestore را راه اندازی کنید
برنامه وب از Firestore برای ذخیره پیام های چت و دریافت پیام های چت جدید استفاده می کند.
در اینجا نحوه راه اندازی Firestore در پروژه Firebase آمده است:
- در پنل سمت چپ کنسول Firebase، Build را گسترش دهید و سپس پایگاه داده Firestore را انتخاب کنید.
- روی ایجاد پایگاه داده کلیک کنید.
- شناسه پایگاه داده را روی
(default)
بگذارید. - یک مکان برای پایگاه داده خود انتخاب کنید، سپس روی Next کلیک کنید.
برای یک برنامه واقعی، می خواهید مکانی را انتخاب کنید که به کاربران شما نزدیک باشد. - در حالت تست روی Start کلیک کنید. سلب مسئولیت در مورد قوانین امنیتی را بخوانید.
بعداً در این کد، قوانین امنیتی را برای ایمن سازی داده های خود اضافه خواهید کرد. بدون افزودن قوانین امنیتی برای پایگاه داده خود، یک برنامه را به صورت عمومی توزیع یا افشا نکنید . - روی ایجاد کلیک کنید.
4. Firebase را پیکربندی کنید
برای استفاده از Firebase با Flutter، باید کارهای زیر را انجام دهید تا پروژه Flutter را برای استفاده صحیح از کتابخانه های FlutterFire
پیکربندی کنید:
- وابستگی های
FlutterFire
را به پروژه خود اضافه کنید. - پلتفرم مورد نظر را در پروژه Firebase ثبت کنید.
- فایل پیکربندی مخصوص پلتفرم را دانلود کنید و سپس آن را به کد اضافه کنید.
در دایرکتوری سطح بالای برنامه Flutter شما، زیر شاخههای android
، ios
، macos
و web
وجود دارد که به ترتیب فایلهای پیکربندی پلتفرم مخصوص iOS و Android را در خود جای میدهند.
پیکربندی وابستگی ها
باید کتابخانه های FlutterFire
را برای دو محصول Firebase که در این برنامه استفاده می کنید اضافه کنید: Authentication و Firestore.
- از خط فرمان، وابستگی های زیر را اضافه کنید:
$ flutter pub add firebase_core
بسته firebase_core
کد مشترک مورد نیاز برای همه پلاگین های Firebase Flutter است.
$ flutter pub add firebase_auth
بسته firebase_auth
ادغام با Authentication را امکان پذیر می کند.
$ flutter pub add cloud_firestore
بسته cloud_firestore
دسترسی به ذخیره سازی داده Firestore را امکان پذیر می کند.
$ flutter pub add provider
بسته firebase_ui_auth
مجموعه ای از ابزارک ها و ابزارهای کمکی را برای افزایش سرعت توسعه دهنده با جریان های احراز هویت ارائه می دهد.
$ flutter pub add firebase_ui_auth
شما بسته های مورد نیاز را اضافه کرده اید، اما همچنین باید پروژه های iOS، Android، macOS و Web runner را برای استفاده مناسب از Firebase پیکربندی کنید. شما همچنین از بسته provider
استفاده می کنید که امکان جداسازی منطق تجاری از منطق نمایش را فراهم می کند.
FlutterFire CLI را نصب کنید
FlutterFire CLI به Firebase CLI زیرین بستگی دارد.
- اگر قبلاً این کار را انجام نداده اید، Firebase CLI را روی دستگاه خود نصب کنید.
- FlutterFire CLI را نصب کنید:
$ dart pub global activate flutterfire_cli
پس از نصب، دستور flutterfire
به صورت جهانی در دسترس است.
برنامه های خود را پیکربندی کنید
CLI اطلاعات پروژه Firebase شما و برنامه های پروژه انتخاب شده را استخراج می کند تا تمام پیکربندی ها را برای یک پلت فرم خاص ایجاد کند.
در ریشه برنامه خود، دستور configure
را اجرا کنید:
$ flutterfire configure
دستور پیکربندی شما را از طریق فرآیندهای زیر راهنمایی می کند:
- یک پروژه Firebase را بر اساس فایل
.firebaserc
یا از کنسول Firebase انتخاب کنید. - پلتفرمهایی را برای پیکربندی، مانند Android، iOS، macOS و وب تعیین کنید.
- برنامه های Firebase را که می توان از آنها پیکربندی استخراج کرد، شناسایی کنید. بهطور پیشفرض، CLI تلاش میکند به طور خودکار برنامههای Firebase را بر اساس پیکربندی پروژه فعلی شما مطابقت دهد.
- یک فایل
firebase_options.dart
در پروژه خود ایجاد کنید.
macOS را پیکربندی کنید
Flutter در macOS برنامههای کاملاً جعبهشنود میسازد. از آنجایی که این برنامه برای برقراری ارتباط با سرورهای Firebase با شبکه یکپارچه می شود، باید برنامه خود را با امتیازات کلاینت شبکه پیکربندی کنید.
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>
برای اطلاعات بیشتر، به پشتیبانی دسکتاپ برای Flutter مراجعه کنید.
5. قابلیت RSVP را اضافه کنید
اکنون که Firebase را به برنامه اضافه کردید، می توانید یک دکمه RSVP ایجاد کنید که افراد را با احراز هویت ثبت می کند. برای اندروید، بومی iOS و وب، بستههای FirebaseUI Auth
از پیش ساخته شدهاند، اما باید این قابلیت را برای Flutter ایجاد کنید.
پروژه ای که قبلاً بازیابی کردید شامل مجموعه ای از ویجت ها بود که رابط کاربری را برای بیشتر جریان احراز هویت پیاده سازی می کند. شما منطق تجاری را برای ادغام احراز هویت با برنامه پیاده سازی می کنید.
منطق کسب و کار را با بسته Provider
اضافه کنید
از بسته provider
استفاده کنید تا یک شیء حالت متمرکز برنامه را در سرتاسر درخت ابزارکهای Flutter برنامه در دسترس قرار دهید:
- یک فایل جدید با نام
app_state.dart
با محتوای زیر ایجاد کنید:
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
Firebase Core و Auth را معرفی میکنند، بسته provider
وارد میکنند که شیء وضعیت برنامه را در سرتاسر درخت ویجت در دسترس قرار میدهد، و ویجتهای احراز هویت را از بسته firebase_ui_auth
شامل میشود.
این شیء وضعیت برنامه کاربردی ApplicationState
یک مسئولیت اصلی برای این مرحله دارد، و آن هشدار دادن به درخت ویجت است که بهروزرسانی یک وضعیت احراز هویت شده وجود دارد.
شما فقط از یک ارائه دهنده برای برقراری ارتباط وضعیت وضعیت ورود کاربر به برنامه استفاده می کنید. برای اینکه به کاربر اجازه ورود به سیستم را بدهید، از رابطهای کاربری ارائه شده توسط بسته firebase_ui_auth
استفاده میکنید، که راهی عالی برای راهاندازی سریع صفحههای ورود به سیستم در برنامههایتان است.
یکپارچه سازی جریان احراز هویت
- واردات در بالای فایل
lib/main.dart
را تغییر دهید:
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';
- وضعیت برنامه را با مقداردهی اولیه برنامه وصل کنید و سپس جریان احراز هویت را به
HomePage
اضافه کنید:
lib/main.dart
void main() {
// Modify from here...
WidgetsFlutterBinding.ensureInitialized();
runApp(ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: ((context, child) => const App()),
));
// ...to here.
}
تغییر تابع main()
باعث میشود بسته ارائهدهنده مسئول نمونهسازی شی وضعیت برنامه با ویجت ChangeNotifierProvider
باشد. شما از این کلاس provider
خاص استفاده میکنید زیرا شیء وضعیت برنامه کلاس ChangeNotifier
را گسترش میدهد، که به بسته provider
اجازه میدهد بداند که چه زمانی ویجتهای وابسته را دوباره نمایش دهد.
- با ایجاد یک پیکربندی
GoRouter
برنامه خود را برای مدیریت پیمایش به صفحات مختلف که FirebaseUI برای شما فراهم می کند، به روز کنید:
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
);
}
}
هر صفحه بر اساس وضعیت جدید جریان احراز هویت، نوع عملکرد متفاوتی دارد. پس از اکثر تغییرات وضعیت در احراز هویت، میتوانید مسیر را به صفحه ترجیحی، چه صفحه اصلی یا یک صفحه دیگر، مانند نمایه، تغییر دهید.
- در روش ساخت کلاس
HomePage
، وضعیت برنامه را با ویجتAuthFunc
ادغام کنید:
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
را نمونه سازی می کنید و آن را در یک ویجت Consumer
قرار می دهید. ویجت Consumer روش معمولی است که بسته provider
را می توان برای بازسازی بخشی از درخت هنگام تغییر وضعیت برنامه استفاده کرد. ویجت AuthFunc
ویجت های تکمیلی است که شما آزمایش می کنید.
جریان احراز هویت را آزمایش کنید
- در برنامه، روی دکمه RSVP ضربه بزنید تا
SignInScreen
را شروع کنید.
- یک آدرس ایمیل وارد کنید. اگر قبلا ثبت نام کرده اید، سیستم از شما می خواهد رمز عبور را وارد کنید. در غیر این صورت، سیستم از شما می خواهد که فرم ثبت نام را تکمیل کنید.
- رمز عبوری کمتر از شش کاراکتر وارد کنید تا جریان رسیدگی به خطا را بررسی کنید. اگر ثبت نام کرده اید، به جای آن رمز عبور را می بینید.
- رمزهای عبور نادرست را برای بررسی جریان رسیدگی به خطا وارد کنید.
- رمز عبور صحیح را وارد کنید. تجربه ورود به سیستم را مشاهده می کنید که به کاربر امکان خروج از سیستم را می دهد.
6. برای Firestore پیام بنویسید
دانستن اینکه کاربران در حال آمدن هستند بسیار خوب است، اما باید به مهمانان کار دیگری بدهید تا در برنامه انجام دهند. اگر آنها بتوانند در دفترچه مهمان پیام بگذارند چه می شود؟ آنها می توانند به اشتراک بگذارند که چرا برای آمدن هیجان زده هستند یا با چه کسی امیدوارند ملاقات کنند.
برای ذخیره پیامهای چتی که کاربران در برنامه مینویسند، از Firestore استفاده میکنید.
مدل داده
Firestore یک پایگاه داده NoSQL است و داده های ذخیره شده در پایگاه داده به مجموعه ها، اسناد، فیلدها و زیر مجموعه ها تقسیم می شود. شما هر پیام چت را به عنوان یک سند در یک مجموعه guestbook
، که یک مجموعه سطح بالا است، ذخیره می کنید.
افزودن پیام به Firestore
در این بخش، قابلیتی را برای ارسال پیام توسط کاربران به پایگاه داده اضافه می کنید. ابتدا یک فیلد فرم و دکمه ارسال را اضافه میکنید و سپس کدی را اضافه میکنید که این عناصر را به پایگاه داده متصل میکند.
- یک فایل جدید به نام
guest_book.dart
ایجاد کنید، یک ویجت حالت دارGuestBook
اضافه کنید تا عناصر رابط کاربری یک فیلد پیام و یک دکمه ارسال بسازید:
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'),
],
),
),
],
),
),
);
}
}
در اینجا چند نکته جالب وجود دارد. ابتدا، فرمی را نمونهسازی میکنید تا بتوانید تأیید کنید که پیام واقعاً حاوی محتوا است و اگر پیام خطایی وجود ندارد، به کاربر نشان دهید. برای تأیید اعتبار یک فرم، با یک GlobalKey
به حالت فرم در پشت فرم دسترسی دارید. برای اطلاعات بیشتر درباره کلیدها و نحوه استفاده از آنها، به زمان استفاده از کلیدها مراجعه کنید.
همچنین به نحوه چیدمان ویجت ها توجه کنید، شما یک Row
با TextFormField
و یک StyledButton
دارید که حاوی یک Row
است. همچنین توجه داشته باشید که TextFormField
در یک ویجت Expanded
پیچیده شده است، که TextFormField
را مجبور می کند هر فضای اضافی را در ردیف پر کند. برای درک بهتر اینکه چرا این مورد نیاز است، به درک محدودیت ها مراجعه کنید.
اکنون که ویجتی دارید که به کاربر امکان می دهد متنی را برای افزودن به کتاب مهمان وارد کند، باید آن را روی صفحه نمایش دهید.
- بدنه
HomePage
را ویرایش کنید تا دو خط زیر را در انتهای فرزندانListView
اضافه کنید:
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)),
در حالی که این برای نمایش ویجت کافی است، اما برای انجام کار مفید کافی نیست. شما این کد را به زودی به روز می کنید تا کاربردی شود.
پیش نمایش برنامه
وقتی کاربر روی SEND کلیک می کند، قطعه کد زیر را فعال می کند. محتویات فیلد ورودی پیام را به مجموعه guestbook
پایگاه داده اضافه می کند. به طور خاص، روش addMessageToGuestBook
محتوای پیام را به یک سند جدید با شناسه ای که به طور خودکار در مجموعه guestbook
تولید می شود، اضافه می کند.
توجه داشته باشید که FirebaseAuth.instance.currentUser.uid
یک مرجع به شناسه منحصر به فرد تولید شده خودکار است که Authentication برای همه کاربران وارد شده ارائه می دهد.
- در فایل
lib/app_state.dart
، متدaddMessageToGuestBook
را اضافه کنید. شما در مرحله بعد این قابلیت را با رابط کاربری متصل می کنید.
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.
}
رابط کاربری و پایگاه داده را متصل کنید
شما یک رابط کاربری دارید که در آن کاربر میتواند متنی را که میخواهد به کتاب مهمان اضافه کند وارد کند و شما کد اضافه کردن ورودی به Firestore را دارید. اکنون تنها کاری که باید انجام دهید این است که این دو را به هم وصل کنید.
- در فایل
lib/home_page.dart
، تغییر زیر را در ویجتHomePage
انجام دهید:
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.
],
),
);
}
}
شما دو خطی که در ابتدای این مرحله اضافه کردید را با اجرای کامل جایگزین کردید. شما مجدداً از Consumer<ApplicationState>
استفاده می کنید تا وضعیت برنامه را برای بخشی از درختی که رندر می کنید در دسترس قرار دهید. این به شما امکان می دهد به شخصی که پیامی را در UI وارد می کند واکنش نشان دهید و آن را در پایگاه داده منتشر کنید. در بخش بعدی، بررسی می کنید که آیا پیام های اضافه شده در پایگاه داده منتشر شده اند یا خیر.
تست ارسال پیام
- در صورت لزوم، وارد برنامه شوید.
- پیامی مانند
Hey there!
و سپس SEND را کلیک کنید.
این عمل پیام را در پایگاه داده Firestore شما می نویسد. با این حال، شما این پیام را در برنامه Flutter واقعی خود نمی بینید زیرا هنوز باید بازیابی داده ها را پیاده سازی کنید، که در مرحله بعد انجام می دهید. با این حال، در داشبورد پایگاه داده کنسول Firebase، می توانید پیام اضافه شده خود را در مجموعه guestbook
ببینید. اگر پیام های بیشتری ارسال کنید، اسناد بیشتری را به مجموعه guestbook
خود اضافه می کنید. به عنوان مثال، قطعه کد زیر را ببینید:
7. پیام ها را بخوانید
دوست داشتنی است که مهمانان می توانند پیام هایی را در پایگاه داده بنویسند، اما هنوز نمی توانند آنها را در برنامه ببینند. زمان رفع آن است!
همگام سازی پیام ها
برای نمایش پیامها، باید شنوندههایی اضافه کنید که هنگام تغییر دادهها فعال شوند و سپس یک عنصر رابط کاربری ایجاد کنید که پیامهای جدید را نشان دهد. شما کدی را به حالت برنامه اضافه می کنید که پیام های تازه اضافه شده از برنامه را گوش می دهد.
- یک فایل
guest_book_message.dart
جدید ایجاد کنید، کلاس زیر را اضافه کنید تا نمای ساختاری دادههایی را که در Firestore ذخیره میکنید نمایش دهید.
lib/guest_book_message.dart
class GuestBookMessage {
GuestBookMessage({required this.name, required this.message});
final String name;
final String message;
}
- در فایل
lib/app_state.dart
، واردات زیر را اضافه کنید:
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
که در آن حالت و دریافت کننده را تعریف می کنید، خطوط زیر را اضافه کنید:
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
، خطوط زیر را اضافه کنید تا زمانی که کاربر وارد سیستم می شود، در یک پرس و جو در مجموعه اسناد مشترک شوید و هنگامی که از سیستم خارج می شود، اشتراک را لغو کنید:
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();
});
}
این بخش مهم است زیرا جایی است که شما یک پرس و جو بر روی مجموعه guestbook
ایجاد می کنید و اشتراک و لغو اشتراک این مجموعه را انجام می دهید. شما به جریان گوش می دهید، جایی که یک حافظه پنهان محلی از پیام های مجموعه guestbook
را بازسازی می کنید و همچنین یک مرجع به این اشتراک ذخیره می کنید تا بتوانید بعداً اشتراک آن را لغو کنید. اینجا خیلی اتفاق می افتد، بنابراین باید آن را در یک دیباگر بررسی کنید تا ببینید چه اتفاقی می افتد تا مدل ذهنی واضح تری به دست آورید. برای اطلاعات بیشتر، به دریافت بهروزرسانیهای بیدرنگ با Firestore مراجعه کنید.
- در فایل
lib/guest_book.dart
، وارد کردن زیر را اضافه کنید:
import 'guest_book_message.dart';
- در ویجت
GuestBook
، فهرستی از پیامها را به عنوان بخشی از پیکربندی اضافه کنید تا این وضعیت در حال تغییر را به رابط کاربری متصل کنید:
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
، روشbuild
را به صورت زیر تغییر دهید تا این پیکربندی نمایان شود:
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()
را با ویجت Column
میپیچید و سپس مجموعهای برای در انتهای فرزندان Column
اضافه میکنید تا یک Paragraph
جدید برای هر پیام در لیست پیامها ایجاد شود.
- برای ساخت صحیح
GuestBook
با پارامترmessages
جدید، بدنهHomePage
را به روز کنید:
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
),
],
],
),
),
تست همگام سازی پیام
Firestore به طور خودکار و فوری داده ها را با مشتریان مشترک پایگاه داده همگام می کند.
همگام سازی پیام آزمایشی:
- در برنامه، پیامهایی را که قبلاً ایجاد کردهاید در پایگاه داده پیدا کنید.
- پیام های جدید بنویسید آنها فورا ظاهر می شوند.
- فضای کاری خود را در چندین پنجره یا تب باز کنید. پیام ها در زمان واقعی در سراسر پنجره ها و تب ها همگام می شوند.
- اختیاری: در منوی پایگاه داده کنسول Firebase، پیام های جدید را به صورت دستی حذف، تغییر دهید یا اضافه کنید. همه تغییرات در UI ظاهر می شود.
تبریک می گویم! اسناد Firestore را در برنامه خود می خوانید!
پیش نمایش برنامه
8. قوانین اساسی امنیتی را تنظیم کنید
شما در ابتدا Firestore را برای استفاده از حالت تست تنظیم کردید، به این معنی که پایگاه داده شما برای خواندن و نوشتن باز است. با این حال، شما فقط باید از حالت تست در مراحل اولیه توسعه استفاده کنید. به عنوان بهترین روش، هنگام توسعه برنامه خود، باید قوانین امنیتی را برای پایگاه داده خود تنظیم کنید. امنیت برای ساختار و رفتار برنامه شما ضروری است.
قوانین امنیتی Firebase به شما امکان می دهد دسترسی به اسناد و مجموعه های موجود در پایگاه داده خود را کنترل کنید. نحو قواعد انعطافپذیر به شما امکان میدهد قوانینی ایجاد کنید که هر چیزی را از تمام نوشتهها تا کل پایگاه داده تا عملیات روی یک سند خاص مطابقت دهد.
قوانین اساسی امنیتی را تنظیم کنید:
- در منوی توسعه کنسول Firebase، روی پایگاه داده > قوانین کلیک کنید. باید قوانین امنیتی پیشفرض زیر و هشداری درباره عمومی بودن قوانین مشاهده کنید:
- مجموعههایی را که برنامه دادهها را روی آنها مینویسد، شناسایی کنید:
در match /databases/{database}/documents
, مجموعه ای را که می خواهید ایمن کنید شناسایی کنید:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /guestbook/{entry} {
// You'll add rules here in the next step.
}
}
از آنجایی که شما از UID احراز هویت به عنوان یک فیلد در هر سند دفترچه مهمان استفاده کردهاید، میتوانید UID احراز هویت را دریافت کنید و تأیید کنید که هر کسی که قصد نوشتن در سند را دارد دارای یک UID تأیید هویت منطبق باشد.
- قوانین خواندن و نوشتن را به مجموعه قوانین خود اضافه کنید:
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;
}
}
}
اکنون، فقط کاربرانی که به سیستم وارد شدهاند میتوانند پیامها را در کتاب مهمان بخوانند، اما فقط نویسنده پیام میتواند یک پیام را ویرایش کند.
- برای اطمینان از وجود تمام فیلدهای مورد انتظار در سند، اعتبارسنجی داده را اضافه کنید:
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. مرحله پاداش: آنچه را که آموخته اید تمرین کنید
وضعیت RSVP یک شرکت کننده را ثبت کنید
در حال حاضر، برنامه شما فقط زمانی به افراد اجازه میدهد که به رویداد چت کنند. همچنین، تنها راهی که میدانید کسی میآید یا نه این است که در چت چنین میگوید.
در این مرحله شما سازماندهی میشوید و به مردم اطلاع میدهید که چند نفر میآیند. شما چند قابلیت را به حالت برنامه اضافه می کنید. اولین مورد این است که یک کاربر که وارد سیستم شده است میتواند نامزد حضور در آن باشد. دومی شمارنده تعداد افراد شرکت کننده است.
- در فایل
lib/app_state.dart
، خطوط زیر را به بخش Accessors درApplicationState
اضافه کنید تا کد UI بتواند با این حالت تعامل داشته باشد:
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});
}
}
- متد
init()
ApplicationState
را به صورت زیر به روز کنید:
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();
});
}
این کد یک جستار همیشه مشترک برای تعیین تعداد شرکت کنندگان و یک پرس و جوی دوم را اضافه می کند که فقط در زمانی که کاربر وارد سیستم شده است فعال است تا مشخص کند آیا کاربر حضور دارد یا خیر.
- شماره زیر را در بالای فایل
lib/app_state.dart
اضافه کنید.
lib/app_state.dart
enum Attending { yes, no, unknown }
- یک فایل جدید
yes_no_selection.dart
ایجاد کنید، یک ویجت جدید تعریف کنید که مانند دکمه های رادیویی عمل می کند:
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'),
),
],
),
);
}
}
}
در حالت نامشخص و نه بله و نه خیر شروع می شود. هنگامی که کاربر انتخاب می کند که آیا شرکت می کند یا خیر، آن گزینه را با یک دکمه پر شده نشان می دهید و گزینه دیگر با یک رندر صاف عقب می نشیند.
- برای استفاده از
YesNoSelection
متدbuild()
HomePage
را بهروزرسانی کنید، یک کاربر وارد شده را قادر میسازد تا نامزد حضور در آن باشد و تعداد شرکتکنندگان را برای رویداد نمایش دهد:
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,
),
],
],
),
),
قوانین را اضافه کنید
قبلاً قوانینی را تنظیم کردهاید، بنابراین دادههایی که با دکمهها اضافه میکنید رد میشوند. شما باید قوانین را به روز کنید تا امکان اضافه شدن به مجموعه attendees
فراهم شود.
- در مجموعه
attendees
، UID احراز هویت را که بهعنوان نام سند استفاده کردهاید بردارید و بررسی کنید کهuid
ارسالکننده با سندی که مینویسد یکسان است:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ... //
match /attendees/{userId} {
allow read: if true;
allow write: if request.auth.uid == userId;
}
}
}
این به همه امکان میدهد فهرست شرکتکنندگان را بخوانند زیرا هیچ داده خصوصی در آنجا وجود ندارد، اما فقط سازنده میتواند آن را بهروزرسانی کند.
- برای اطمینان از وجود تمام فیلدهای مورد انتظار در سند، اعتبارسنجی داده را اضافه کنید:
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;
}
}
}
- اختیاری: در برنامه، روی دکمهها کلیک کنید تا نتایج را در داشبورد Firestore در کنسول Firebase ببینید.
پیش نمایش برنامه
10. تبریک می گویم!
شما از Firebase برای ساختن یک برنامه وب تعاملی و بلادرنگ استفاده کردید!