Created
September 11, 2019 14:51
-
-
Save Pregum/c8b56f88516ee9a8202ac3a0e69f0b10 to your computer and use it in GitHub Desktop.
hatena blog用 (flutter_stethoでDBの中身を見る)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter_localizations/flutter_localizations.dart'; | |
import 'package:flutter_stetho/flutter_stetho.dart'; | |
import 'package:intl/intl.dart'; | |
import 'package:path/path.dart'; | |
import 'package:path_provider/path_provider.dart'; | |
import 'package:sqflite/sqflite.dart'; | |
import 'package:synchronized/synchronized.dart'; | |
/// DB操作 | |
class DbProvider { | |
Database db; | |
final _lock = Lock(); | |
Future<Database> getDb() async { | |
db ??= await _lock.synchronized(() async => | |
db ?? | |
await _openDatabase(join( | |
await getApplicationDocumentsDirectory() | |
.then((onValue) => onValue.parent.path), | |
"databases/foodCalendar.db.sqlite"))); | |
return db; | |
} | |
Future<Database> _openDatabase(String path) { | |
return openDatabase( | |
path, | |
version: 1, | |
onCreate: (Database newDb, int version) { | |
newDb.execute(""" | |
CREATE TABLE Food | |
( | |
id INTEGER PRIMARY KEY NOT NULL, | |
name TEXT, | |
limitDate TEXT, | |
usedDate TEXT, | |
T_kindType_ID INTEGER | |
); | |
"""); | |
}, | |
); | |
} | |
Future<int> delete(int index) async { | |
var id = await db.delete('Food', where: 'id = ?', whereArgs: [index]); | |
print(id); | |
return id; | |
} | |
Future<int> insertNewFood(FoodRecord record) async { | |
int id = await db.rawInsert( | |
'INSERT INTO Food(name, limitDate, usedDate, T_kindType_ID) VALUES(?, ?, ?, ?)', | |
[ | |
record.name, | |
DateFormat('yyyy-MM-dd').format(record.limitDate), | |
DateFormat('yyyy-MM-dd').format(record.usedDate), | |
record.kindTypeId | |
], | |
); | |
return id; | |
} | |
Future<int> update(FoodRecord updateRecord) async { | |
int id = await db.update('Food', updateRecord.toMap(), | |
where: 'id = ?', whereArgs: [updateRecord.id]); | |
print('update to : $id'); | |
return id; | |
} | |
Future<List<Map<String, dynamic>>> getQuery() async { | |
Database d = await this.getDb(); | |
Future<List<Map<String, dynamic>>> list = d.rawQuery('SELECT * FROM Food'); | |
return list; | |
} | |
} | |
class FoodRecord { | |
int id; | |
String name; | |
DateTime limitDate; | |
DateTime usedDate; | |
int kindTypeId; | |
FoodRecord( | |
{this.id, this.name, this.limitDate, this.usedDate, this.kindTypeId}); | |
Map<String, dynamic> toMap() { | |
return { | |
'id': id, | |
'name': name, | |
'limitDate': DateFormat('yyyy-MM-dd').format(limitDate), | |
'usedDate': DateFormat('yyyy-MM-dd').format(usedDate), | |
'T_kindType_ID': kindTypeId, | |
}; | |
} | |
} | |
void main() { | |
Stetho.initialize(); | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Database Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(title: 'Flutter DataBase Demo'), | |
localizationsDelegates: [ | |
GlobalMaterialLocalizations.delegate, | |
GlobalWidgetsLocalizations.delegate, | |
], | |
supportedLocales: [ | |
const Locale("en"), | |
const Locale("ja"), | |
], | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key key, this.title}) : super(key: key); | |
final String title; | |
@override | |
MyHomePageState createState() => MyHomePageState(); | |
} | |
class MyHomePageState extends State<MyHomePage> { | |
DbProvider _dbProvider = DbProvider(); | |
Widget createGrid() { | |
return FutureBuilder( | |
future: _dbProvider.getQuery(), | |
builder: (BuildContext context, AsyncSnapshot<List<Map>> snapshot) { | |
// FloatingActionButtonを押すと、データが表示される。 | |
if (!snapshot.hasData) { | |
return Center(child: CircularProgressIndicator()); | |
} | |
if (snapshot.data.isEmpty) { | |
return Center( | |
child: Text('右下の追加ボタンから新しいデータを追加して下さい。'), | |
); | |
} | |
return GridView.count( | |
crossAxisCount: 2, | |
children: List.generate(snapshot.data.length, (index) { | |
return GestureDetector( | |
onTap: () { | |
Navigator.push( | |
context, | |
MaterialPageRoute( | |
builder: (context) => | |
DetailsPage(snapshot.data[index]))).then((result) { | |
// resultに遷移後のぺージから値を取得します。 | |
if (result != null) { | |
setState(() { | |
print('変更後のname : ${result.name.toString()}'); | |
_dbProvider.update(result); | |
print('更新しました!, id: ${result.id}'); | |
}); | |
} else | |
print('キャンセルされました'); | |
}); | |
}, | |
child: Card( | |
color: | |
DateTime.parse(snapshot.data[index]['limitDate'].toString()) | |
.isBefore(DateTime.now()) | |
? Colors.red | |
: Colors.lightGreen[200], | |
child: Center( | |
child: Container( | |
margin: EdgeInsets.only(left: 10.0), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.stretch, | |
children: <Widget>[ | |
Row( | |
children: <Widget>[ | |
Expanded( | |
child: Text( | |
'${snapshot.data[index]['name'].toString()}', | |
style: TextStyle( | |
fontSize: 16, fontWeight: FontWeight.bold), | |
), | |
), | |
IconButton( | |
alignment: Alignment.center, | |
icon: Icon(Icons.clear), | |
onPressed: () { | |
int id = snapshot.data[index]['id']; | |
setState(() { | |
_dbProvider.delete(id); | |
}); | |
}, | |
), | |
], | |
), | |
// データベース内のデータを表示 | |
Divider(), | |
Text(' No : ${index + 1}'), | |
Text( | |
' 期限日 : ${snapshot.data[index]['limitDate'].toString()}'), | |
Text( | |
' 使用日 : ${snapshot.data[index]['usedDate'].toString()}'), | |
], | |
), | |
), | |
), | |
), | |
); | |
}), | |
); | |
}, | |
); | |
} | |
@override | |
Widget build(BuildContext context) { | |
var now = DateTime.now(); | |
var today = DateTime(now.year, now.month, now.day); | |
var limiltDate = | |
DateTime(now.year, now.month, now.add(Duration(days: 7)).day); | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(widget.title), | |
), | |
// GridViewを作成 | |
body: createGrid(), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
Navigator.push( | |
context, | |
MaterialPageRoute( | |
builder: (context) => CreatingItemPage(FoodRecord( | |
name: "steak", | |
limitDate: limiltDate, | |
usedDate: today, | |
kindTypeId: 1)))).then((result) async { | |
if (result != null) { | |
print('追加されました.'); | |
int id = await _dbProvider.insertNewFood(result); | |
print("追加されたId: $id"); | |
} | |
}); | |
}, | |
tooltip: '追加', | |
child: Icon(Icons.add), | |
), // This trailing comma makes auto-formatting nicer for build methods. | |
); | |
} | |
} | |
/// 新規作成用ページです。 | |
class CreatingItemPage extends StatefulWidget { | |
FoodRecord foodRecord; | |
CreatingItemPage(this.foodRecord); | |
@override | |
State<StatefulWidget> createState() => CreatingItemPageState(this.foodRecord); | |
} | |
class CreatingItemPageState extends State<CreatingItemPage> { | |
CreatingItemPageState(this.foodRecord); | |
FoodRecord foodRecord; | |
String tmp; | |
bool isNameEmpty; | |
final DateFormat _dateFormat = DateFormat('yyyy-MM-dd'); | |
TextEditingController _textEditingController; | |
@override | |
void initState() { | |
super.initState(); | |
_textEditingController = TextEditingController(text: this.foodRecord.name); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('新規作成画面'), | |
), | |
body: SafeArea( | |
child: Container( | |
margin: EdgeInsets.all(10.0), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: <Widget>[ | |
Text('食材名'), | |
TextField( | |
controller: _textEditingController, | |
decoration: InputDecoration( | |
border: OutlineInputBorder(), | |
hintText: '食材名を入力してください。', | |
), | |
textAlign: TextAlign.start, | |
onChanged: (text) { | |
setState(() { | |
this.foodRecord.name = text; | |
this.isNameEmpty = text.isEmpty; | |
}); | |
print('name: ${this.foodRecord.name}'); | |
}, | |
), | |
Text( | |
'期限日', | |
textAlign: TextAlign.left, | |
), | |
FlatButton( | |
color: Color.fromARGB(255, 200, 200, 200), | |
child: Text('${_dateFormat.format(this.foodRecord.limitDate)}'), | |
onPressed: () async { | |
var selectedDate = await showDatePicker( | |
context: context, | |
locale: const Locale("ja"), | |
initialDate: this.foodRecord.limitDate, | |
firstDate: DateTime(1900), | |
lastDate: DateTime(2100), | |
builder: (BuildContext context, Widget widget) => widget, | |
); | |
print('limitDate: ${this.foodRecord.limitDate}'); | |
if (selectedDate != null) { | |
setState(() { | |
this.foodRecord.limitDate = selectedDate; | |
}); | |
} | |
print('selectedDate: $selectedDate'); | |
}, | |
), | |
Text('使用日'), | |
FlatButton( | |
color: Color.fromARGB(255, 200, 200, 200), | |
child: Text('${_dateFormat.format(this.foodRecord.usedDate)}'), | |
onPressed: () async { | |
var selectedDate = await showDatePicker( | |
context: context, | |
locale: const Locale("ja"), | |
initialDate: this.foodRecord.usedDate, | |
firstDate: DateTime(1900), | |
lastDate: DateTime(2100), | |
builder: (BuildContext context, Widget widget) => widget, | |
); | |
if (selectedDate != null) { | |
setState(() { | |
this.foodRecord.usedDate = selectedDate; | |
}); | |
} | |
print('${this.foodRecord.usedDate}'); | |
}, | |
), | |
Expanded( | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: <Widget>[ | |
RaisedButton( | |
child: Text('キャンセル'), | |
onPressed: () { | |
Navigator.pop(context); | |
}, | |
), | |
RaisedButton( | |
child: Text('追加'), | |
onPressed: (this.foodRecord.name.trim().isNotEmpty) | |
? () { | |
Navigator.pop(context, this.foodRecord); | |
} | |
: null, | |
), | |
], | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
/// 遷移先のページです。 | |
/// 選択した項目の詳細を表示します。 | |
class DetailsPage extends StatefulWidget { | |
/// コンストラクタです。 | |
DetailsPage(this._dynamicContent); | |
final dynamic _dynamicContent; | |
@override | |
State<StatefulWidget> createState() => new DetailsPageState(_dynamicContent); | |
} | |
class DetailsPageState extends State<DetailsPage> { | |
/// コンストラクタです。 | |
DetailsPageState(dynamic dynamicContent) { | |
_foodRecord = FoodRecord( | |
id: dynamicContent['id'], | |
name: dynamicContent['name'], | |
limitDate: DateTime.parse(dynamicContent['limitDate'].toString()), | |
usedDate: DateTime.parse(dynamicContent['usedDate'].toString()), | |
kindTypeId: dynamicContent['T_kindType_ID']); | |
_textEditingController = TextEditingController(text: _foodRecord.name); | |
} | |
/// 食材データを遷移元と受け渡しを行うオブジェクトです。 | |
FoodRecord _foodRecord; | |
TextEditingController _textEditingController; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('詳細画面'), | |
), | |
body: Column( | |
children: <Widget>[ | |
Container( | |
margin: EdgeInsets.all(10.0), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: <Widget>[ | |
Text('食材名'), | |
TextField( | |
controller: _textEditingController, | |
decoration: InputDecoration( | |
border: OutlineInputBorder(), hintText: '食材名を入力してください。'), | |
textAlign: TextAlign.start, | |
onChanged: (text) { | |
setState(() { | |
print('text : $text'); | |
this._foodRecord.name = text; | |
// this._isNameEmpty = text.isEmpty; | |
}); | |
}, | |
), | |
Text('期限日'), | |
FlatButton( | |
color: Color.fromARGB(255, 200, 200, 200), | |
child: Text( | |
'${DateFormat("yyyy-MM-dd").format(_foodRecord.limitDate)}'), | |
onPressed: () async { | |
var selectedDate = await showDatePicker( | |
context: context, | |
locale: const Locale("ja"), | |
firstDate: DateTime(1900), | |
initialDate: _foodRecord.limitDate, | |
lastDate: DateTime(2100), | |
builder: (BuildContext build, Widget widget) => widget, | |
); | |
if (selectedDate != null) { | |
setState(() { | |
_foodRecord.limitDate = selectedDate; | |
}); | |
} | |
}, | |
), | |
Text('使用日'), | |
FlatButton( | |
color: Color.fromARGB(255, 200, 200, 200), | |
child: Text( | |
'${DateFormat("yyyy-MM-dd").format(_foodRecord.usedDate)}'), | |
onPressed: () async { | |
var selectedDate = await showDatePicker( | |
context: context, | |
locale: const Locale("ja"), | |
firstDate: DateTime(1900), | |
initialDate: _foodRecord.usedDate, | |
lastDate: DateTime(2100), | |
builder: (BuildContext build, Widget widget) => widget, | |
); | |
if (selectedDate != null) { | |
_foodRecord.usedDate = selectedDate; | |
} | |
}, | |
), | |
], | |
), | |
), | |
Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: <Widget>[ | |
RaisedButton( | |
child: Text('キャンセル'), | |
onPressed: () { | |
Navigator.pop(context); | |
}, | |
), | |
RaisedButton( | |
child: Text(' 変更 '), | |
onPressed: _foodRecord.name.isEmpty | |
? null // RaisedButton.onPressedにnullを入れることでボタンを操作不可にします。 | |
: () { | |
Navigator.pop<FoodRecord>(context, this._foodRecord); | |
}, | |
), | |
], | |
), | |
], | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: db_study | |
description: A new Flutter project. | |
# The following defines the version and build number for your application. | |
# A version number is three numbers separated by dots, like 1.2.43 | |
# followed by an optional build number separated by a +. | |
# Both the version and the builder number may be overridden in flutter | |
# build by specifying --build-name and --build-number, respectively. | |
# In Android, build-name is used as versionName while build-number used as versionCode. | |
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning | |
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. | |
# Read more about iOS versioning at | |
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html | |
version: 1.0.0+1 | |
environment: | |
sdk: ">=2.1.0 <3.0.0" | |
dependencies: | |
flutter: | |
sdk: flutter | |
# The following adds the Cupertino Icons font to your application. | |
# Use with the CupertinoIcons class for iOS style icons. | |
cupertino_icons: ^0.1.2 | |
sqflite: any | |
path_provider: any | |
# DateTimeフォーマット用ライブラリ | |
intl: ^0.15.8 | |
# DatePicker日本語化用ライブラリ | |
flutter_localizations: | |
sdk: flutter | |
flutter_stetho: ^0.3.0 | |
dev_dependencies: | |
flutter_test: | |
sdk: flutter | |
# For information on the generic Dart part of this file, see the | |
# following page: https://www.dartlang.org/tools/pub/pubspec | |
# The following section is specific to Flutter. | |
flutter: | |
# The following line ensures that the Material Icons font is | |
# included with your application, so that you can use the icons in | |
# the material Icons class. | |
uses-material-design: true | |
# To add assets to your application, add an assets section, like this: | |
# assets: | |
# - images/a_dot_burr.jpeg | |
# - images/a_dot_ham.jpeg | |
# An image asset can refer to one or more resolution-specific "variants", see | |
# https://flutter.dev/assets-and-images/#resolution-aware. | |
# For details regarding adding assets from package dependencies, see | |
# https://flutter.dev/assets-and-images/#from-packages | |
# To add custom fonts to your application, add a fonts section here, | |
# in this "flutter" section. Each entry in this list should have a | |
# "family" key with the font family name, and a "fonts" key with a | |
# list giving the asset and other descriptors for the font. For | |
# example: | |
# fonts: | |
# - family: Schyler | |
# fonts: | |
# - asset: fonts/Schyler-Regular.ttf | |
# - asset: fonts/Schyler-Italic.ttf | |
# style: italic | |
# - family: Trajan Pro | |
# fonts: | |
# - asset: fonts/TrajanPro.ttf | |
# - asset: fonts/TrajanPro_Bold.ttf | |
# weight: 700 | |
# | |
# For details regarding fonts from package dependencies, | |
# see https://flutter.dev/custom-fonts/#from-packages |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment