Я изучал на флаттере, как выполнять CRUD-операции, используя паттерн BLo C, я увидел онлайн-учебник и попытался его применить. C, R, U в порядке, но удаление не работает. это исходный код Пожалуйста помоги !
файл todo_blo c .dart
import 'package:zencartos/models/todo.dart';
import 'package:zencartos/repository/todo_repository.dart';
class TodoBloc {
//Get instance of the Repository
final _todoRepository = TodoRepository();
final _todoController = StreamController<List<Todo>>.broadcast();
get todos => _todoController.stream;
TodoBloc() {
getTodos();
}
getTodos({String query}) async {
_todoController.sink.add(await _todoRepository.getAllTodos(query: query));
}
addTodo(Todo todo) async {
await _todoRepository.insertTodo(todo);
getTodos();
}
updateTodo(Todo todo) async {
await _todoRepository.updateTodo(todo);
getTodos();
}
deleteTodoById(int id) async {
_todoRepository.deleteTodoById(id);
getTodos();
}
dispose() {
_todoController.close();
}
}
Страница DAO todo_dao.dart
import 'package:zencartos/database/database.dart';
import 'package:zencartos/models/todo.dart';
class TodoDao {
final dbProvider = DatabaseProvider.dbProvider;
Future<int> createTodo(Todo todo) async{
final db = await dbProvider.database;
var result = db.insert(todoTABLE, todo.toDatabaseJson());
return result;
}
Future<List<Todo>> getTodos({List<String> columns, String query})
async{
final db = await dbProvider.database;
List<Map<String, dynamic>> result;
if (query != null){
if (query.isNotEmpty)
result = await db.query(todoTABLE, columns: columns, where: 'description LIKE ?', whereArgs: ["%$query%"]);
} else {
result = await db.query(todoTABLE, columns: columns);
}
List<Todo> todos = result.isNotEmpty
? result.map((item)=> Todo.fromDatabaseJson(item)).toList()
: [];
return todos;
}
//Update Todo record
Future<int> updateTodo(Todo todo) async{
final db = await dbProvider.database;
var result = await db.update(todoTABLE, todo.toDatabaseJson(),
where: "id = ?", whereArgs: [todo.id]);
return result;
}
//Delete Todo records
Future<int> deleteTodo(int id) async{
final db = await dbProvider.database;
var result = await db.delete(todoTABLE, where: 'id = ?', whereArgs: [id]);
return result;
}
}
Страница базы данных database.dart
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
final todoTABLE = 'Todo';
class DatabaseProvider {
static final DatabaseProvider dbProvider = DatabaseProvider();
Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await createDatabase();
return _database;
}
createDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "ReactiveTodo");
var database = await openDatabase(path, version: 1, onCreate: initDB, onUpgrade: onUpgrade);
return database;
}
void onUpgrade(Database database, int oldVersion, int newVersion){
if (newVersion > oldVersion){}
}
void initDB(Database database, int version) async{
await database.execute("CREATE TABLE $todoTABLE ("
"id INTEGER PRIMARY KEY, "
"description TEXT, "
"is_done INTEGER "
")");
}
}
Страница модели todo.dart
class Todo{
int id;
String description;
bool isDone = false;
Todo({this.id, this.description, this.isDone = false});
factory Todo.fromDatabaseJson(Map<String, dynamic> data) => Todo(
id: data['data'],
description: data['description'],
isDone: data['is_done'] == 0 ? false : true,
);
Map<String, dynamic> toDatabaseJson() => {
"id": this.id,
"description": this.description,
"is_done": this.isDone == false ? 0 : 1,
};
}
Просмотр страницы home_Page2.dart
import 'package:flutter/services.dart';
import 'package:zencartos/bloc/todo_bloc.dart';
import 'package:zencartos/models/todo.dart';
class HomePage2 extends StatelessWidget {
HomePage2({Key key, this.title}) : super(key: key);
final TodoBloc todoBloc = TodoBloc();
final String title;
//Allows Todo card to be dismissable horizontally
final DismissDirection _dismissDirection = DismissDirection.horizontal;
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark.copyWith(
statusBarColor: Colors.white,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark));
return Scaffold(
resizeToAvoidBottomPadding: false,
body: SafeArea(
child: Container(
color: Colors.white,
padding:
const EdgeInsets.only(left: 2.0, right: 2.0, bottom: 2.0),
child: Container(
//This is where the magic starts
child: getTodosWidget()))),
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey, width: 0.3),
)),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: Icon(
Icons.menu,
color: Colors.indigoAccent,
size: 28,
),
onPressed: () {
//just re-pull UI for testing purposes
todoBloc.getTodos();
}),
Expanded(
child: Text(
"Todo",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontFamily: 'RobotoMono',
fontStyle: FontStyle.normal,
fontSize: 19),
),
),
Wrap(children: <Widget>[
IconButton(
icon: Icon(
Icons.search,
size: 28,
color: Colors.indigoAccent,
),
onPressed: () {
_showTodoSearchSheet(context);
},
),
Padding(
padding: EdgeInsets.only(right: 5),
)
])
],
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: Padding(
padding: EdgeInsets.only(bottom: 25),
child: FloatingActionButton(
elevation: 5.0,
onPressed: () {
_showAddTodoSheet(context);
},
backgroundColor: Colors.white,
child: Icon(
Icons.add,
size: 32,
color: Colors.indigoAccent,
),
),
));
}
void _showAddTodoSheet(BuildContext context) {
final _todoDescriptionFormController = TextEditingController();
showModalBottomSheet(
context: context,
builder: (builder) {
return new Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: new Container(
color: Colors.transparent,
child: new Container(
height: 230,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0),
topRight: const Radius.circular(10.0))),
child: Padding(
padding: EdgeInsets.only(
left: 15, top: 25.0, right: 15, bottom: 30),
child: ListView(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
controller: _todoDescriptionFormController,
textInputAction: TextInputAction.newline,
maxLines: 4,
style: TextStyle(
fontSize: 21, fontWeight: FontWeight.w400),
autofocus: true,
decoration: const InputDecoration(
hintText: 'I have to...',
labelText: 'New Todo',
labelStyle: TextStyle(
color: Colors.indigoAccent,
fontWeight: FontWeight.w500)),
validator: (String value) {
if (value.isEmpty) {
return 'Empty description!';
}
return value.contains('')
? 'Do not use the @ char.'
: null;
},
),
),
Padding(
padding: EdgeInsets.only(left: 5, top: 15),
child: CircleAvatar(
backgroundColor: Colors.indigoAccent,
radius: 18,
child: IconButton(
icon: Icon(
Icons.save,
size: 22,
color: Colors.white,
),
onPressed: () {
final newTodo = Todo(
description:
_todoDescriptionFormController
.value.text);
if (newTodo.description.isNotEmpty) {
/*Create new Todo object and make sure
the Todo description is not empty,
because what's the point of saving empty
Todo
*/
todoBloc.addTodo(newTodo);
//dismisses the bottomsheet
Navigator.pop(context);
}
},
),
),
)
],
),
],
),
),
),
),
);
});
}
void _showTodoSearchSheet(BuildContext context) {
final _todoSearchDescriptionFormController = TextEditingController();
showModalBottomSheet(
context: context,
builder: (builder) {
return new Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: new Container(
color: Colors.transparent,
child: new Container(
height: 230,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0),
topRight: const Radius.circular(10.0))),
child: Padding(
padding: EdgeInsets.only(
left: 15, top: 25.0, right: 15, bottom: 30),
child: ListView(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
controller: _todoSearchDescriptionFormController,
textInputAction: TextInputAction.newline,
maxLines: 4,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w400),
autofocus: true,
decoration: const InputDecoration(
hintText: 'Search for todo...',
labelText: 'Search *',
labelStyle: TextStyle(
color: Colors.indigoAccent,
fontWeight: FontWeight.w500),
),
validator: (String value) {
return value.contains('@')
? 'Do not use the @ char.'
: null;
},
),
),
Padding(
padding: EdgeInsets.only(left: 5, top: 15),
child: CircleAvatar(
backgroundColor: Colors.indigoAccent,
radius: 18,
child: IconButton(
icon: Icon(
Icons.search,
size: 22,
color: Colors.white,
),
onPressed: () {
/*This will get all todos
that contains similar string
in the textform
*/
todoBloc.getTodos(
query:
_todoSearchDescriptionFormController
.value.text);
//dismisses the bottomsheet
Navigator.pop(context);
},
),
),
)
],
),
],
),
),
),
),
);
});
}
Widget getTodosWidget() {
return StreamBuilder(
stream: todoBloc.todos,
builder: (BuildContext context, AsyncSnapshot<List<Todo>> snapshot) {
return getTodoCardWidget(snapshot);
},
);
}
Widget getTodoCardWidget(AsyncSnapshot<List<Todo>> snapshot) {
if (snapshot.hasData) {
return snapshot.data.length != 0
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, itemPosition) {
Todo todo = snapshot.data[itemPosition];
final Widget dismissibleCard = new Dismissible(
background: Container(
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"Deleting",
style: TextStyle(color: Colors.white),
),
),
),
color: Colors.redAccent,
),
onDismissed: (direction) {
todoBloc.deleteTodoById(todo.id);
},
direction: _dismissDirection,
key: new ObjectKey(todo),
child: Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.grey[200], width: 0.5),
borderRadius: BorderRadius.circular(5),
),
color: Colors.white,
child: ListTile(
leading: InkWell(
onTap: () {
//Reverse the value
todo.isDone = !todo.isDone;
todoBloc.updateTodo(todo);
},
child: Container(
//decoration: BoxDecoration(),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: todo.isDone
? Icon(
Icons.done,
size: 26.0,
color: Colors.indigoAccent,
)
: Icon(
Icons.check_box_outline_blank,
size: 26.0,
color: Colors.tealAccent,
),
),
),
),
title: Text(
todo.description,
style: TextStyle(
fontSize: 16.5,
fontFamily: 'RobotoMono',
fontWeight: FontWeight.w500,
decoration: todo.isDone
? TextDecoration.lineThrough
: TextDecoration.none),
),
)),
);
return dismissibleCard;
},
)
: Container(
child: Center(
//this is used whenever there 0 Todo
//in the data base
child: noTodoMessageWidget(),
));
} else {
return Center(
child: loadingData(),
);
}
}
Widget loadingData() {
todoBloc.getTodos();
return Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
Text("Loading...",
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w500))
],
),
),
);
}
Widget noTodoMessageWidget() {
return Container(
child: Text(
"Start adding Todo...",
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w500),
),
);
}
dispose() {
todoBloc.dispose();
}
}
Страница хранилища todo_repository.dart
import 'package:zencartos/models/todo.dart';
class TodoRepository {
final todoDao = TodoDao();
Future getAllTodos({String query}) => todoDao.getTodos(query: query);
Future insertTodo(Todo todo) => todoDao.createTodo(todo);
Future updateTodo(Todo todo) => todoDao.updateTodo(todo);
Future deleteTodoById(int id) => todoDao.deleteTodo(id);
//We are not going to use this in the demo
//Future deleteAllTodos() => todoDao.deleteAllTodos();
}