完成イメージ
作業手順
- firebaseをアプリに追加
- firestoreを設定
- ソースコード書き書き(Firestoreデータの読み取り)
firebaseをflutterアプリに設定
こちらに関しては公式のほうが丁寧に説明されておりますので以下のGoogle公式サイトを参照してください。
Flutter アプリに Firebase を追加する
firestoreを設定
Firestore プラグインの追加
pubspec.yaml
dependencies:
flutter:
sdk: flutter
firebase_core: ^0.3.0
cloud_firestore: ^0.9.5
パッケージのimport
main.dart
import 'package:cloud_firestore/cloud_firestore.dart';
トップ画面の読み取り
要点はStreamBuilderを使用して、'category'という名前のテーブルを取得するところです。
Firestore.instance.collection('category').snapshots()
また、アイテムをタップ時にタップされたアイテムの情報を画面遷移の際に以下のように渡しています。
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => TangoListDetailScreen(categoryName: categolies[index]["name"], categoryID: categolies[index].documentID,)),
);
}
main.dart
@override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
color: Colors.white
),
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('category').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Text('Loading...');
default:
List<DocumentSnapshot> categolies = snapshot.data.documents;
return ListView.builder(
padding: EdgeInsets.fromLTRB(30, 10, 30, 10),
itemCount: categolies.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
child: Container(
margin: EdgeInsets.only(bottom: 20.0),
height: 97,
decoration: new BoxDecoration(
boxShadow: [BoxShadow(
color: Color(0x29000000),
offset: Offset(0,3),
blurRadius: 6,
spreadRadius: 0
) ],
),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Container(
decoration: new BoxDecoration(
color: Color(0xffffffff),
border: Border.all(
color: Color(0xff007aff),
width: 0.5
),
),
child: Center(
child: Text(
categolies[index]["name"],
style: TextStyle(
fontSize: 25,
)
),
)
),
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => TangoListDetailScreen(categoryName: categolies[index]["name"], categoryID: categolies[index].documentID,)),
);
}
);
},
);
}
},
),
);
}
単語一覧画面の設定
import 'package:flutter/material.dart';
import 'TangoScreen.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class TangoListDetailScreen extends StatelessWidget {
TangoListDetailScreen({this.categoryName, this.categoryID});
final String categoryName;
final String categoryID;
@override
Widget build(BuildContext context) {
return Scaffold(
body: TangoListDetailWidget(categoryName: categoryName, categoryID: categoryID,),
);
}
}
class Header extends StatelessWidget with PreferredSizeWidget{
Header({this.title});
final String title;
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
return AppBar(
iconTheme: IconThemeData(
color: Colors.black,
),
title: Text(
title ?? '',
style: TextStyle(
color: Colors.black,
fontSize: 20,
),
),
backgroundColor: Colors.grey[200],
centerTitle: true,
elevation: 0.0,
);
}
}
class TangoListDetailWidget extends StatefulWidget {
TangoListDetailWidget({this.categoryName, this.categoryID});
final String categoryName;
final String categoryID;
@override
State<StatefulWidget> createState() {
return ListState(categoryName: categoryName, categoryID: categoryID);
}
}
class ListState extends State<TangoListDetailWidget> {
ListState({this.categoryName, this.categoryID});
final String categoryName;
final String categoryID;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: Header(title: categoryName),
body: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('words').where('categories', arrayContains: categoryID).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Text('Loading...');
default:
List<DocumentSnapshot> words = snapshot.data.documents;
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Container(
width: 375,
height: 110,
decoration: new BoxDecoration(
color: Color(0xff098a8c),
border: Border.all(
color: Color(0xff098a8c),
width: 1
)
),
child: Container(
margin: EdgeInsets.only(right: 10, bottom: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
verticalDirection: VerticalDirection.up,
textDirection: TextDirection.rtl,
children: [
Expanded(
child: Text(categoryName,
style: TextStyle(
fontFamily: 'SFProDisplay',
color: Color(0xffffffff),
fontSize: 30,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal,
letterSpacing: 0.0075,
),
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
),
),
]
),
),
);
}
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Colors.black38),
),
),
child: ListTile(
trailing: Image.asset('assets/right_detail_grey.png'),
title: Text(
words[index - 1]["indonesia"],
overflow: TextOverflow.ellipsis,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => TangoScreen(categoryName: categoryName, categoryID: categoryID, currentIndex: index - 1,)),
);
},
));},
itemCount: words.length + 1,
);
}
},
),
);
}
}
単語画面の設定
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class TangoScreen extends StatelessWidget {
TangoScreen({this.categoryName, this.categoryID, this.currentIndex});
final String categoryName;
final String categoryID;
final int currentIndex;
@override
Widget build(BuildContext context) {
return Scaffold(
body: TangoWidget(categoryName: categoryName, categoryID: categoryID, currentIndex: currentIndex),
);
}
}
class Header extends StatelessWidget with PreferredSizeWidget{
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
return AppBar(
iconTheme: IconThemeData(
color: Colors.black,
),
actions: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Row(
children: [
GestureDetector(
child: Container(
margin: EdgeInsets.only(left: 5.0, right: 5.0),
child: Image.asset('assets/checkmark_lightgrey.png'),
),
onTap: () {
},
),
],
)
),
],
backgroundColor: Colors.white,
centerTitle: true,
elevation: 0.0,
);
}
}
class TangoWidget extends StatefulWidget {
TangoWidget({this.categoryName, this.categoryID, this.currentIndex});
final String categoryName;
final String categoryID;
final int currentIndex;
@override
State<StatefulWidget> createState() {
return TangoState(categoryName: categoryName, categoryID: categoryID, currentIndex: currentIndex);
}
}
class TangoState extends State<TangoWidget> {
TangoState({this.categoryName, this.categoryID, this.currentIndex});
final String categoryName;
final String categoryID;
int currentIndex = 0;
bool isFront = true;
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('words').where('categories', arrayContains: categoryID).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Text('Loading...');
default:
List<DocumentSnapshot> words = snapshot.data.documents;
return Scaffold(
appBar: Header(),
body: Container(
alignment: Alignment.center,
child: Text(
isFront ? words[currentIndex]["indonesia"] : words[currentIndex]["japanese"],
style: TextStyle(
fontFamily: 'SFProDisplay',
color: Colors.black,
fontSize: 40,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal,
letterSpacing: 0.0075,
),
overflow: TextOverflow.visible,
),
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
child: Container(
margin: EdgeInsets.all(15),
child: Image.asset('assets/left_arrow_big.png'),
),
onTap: currentIndex == 0 ? null : () {
setState(() {
isFront = true;
currentIndex--;
});
},
),
GestureDetector(
child: Container(
margin: EdgeInsets.all(15),
child: Image.asset('assets/return.png'),
),
onTap: () {
setState(() {
isFront = !isFront;
});
},
),
GestureDetector(
child: Container(
margin: EdgeInsets.all(15),
child: Image.asset('assets/right_arrow_big.png'),
),
onTap: currentIndex + 1 >= words.length ? null : () {
setState(() {
isFront = true;
currentIndex++;
});
},
),
],
),
),
);
}
},
);
}
}