-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR includes changes and new features to address the issue of the keyboard overlap on the sheet. Once the PR is merged, the framework will automatically offset the sheet by the keyboard height to prevent the content from being overlapped by the keyboard. ## Changes made ### Breaking changes - Change the type of `SheetMetrics.viewportDimensions` from `Size` to `ViewportDimensions`, which holds the `MediaQueryData.viewInsets` of the window (typically the keyboard height) in addition to the viewport size. ### Internal changes - Change `SheetViewport` to gradually offset the sheet by the keyboard height. - Split `_ContentScrollDrivenSheetActivity` into the 3 parts according to the lifecycle; `_ContentIdleScrollSheetActivity`, `_ContentUserScrollSheetActivity` and `_ContentBallisticScrollSheetActivity`. - Change some sheet activities such as `UserDragSheetActivity` and `_ContentUserScrollSheetActivity` to keep the visual position of the sheet unchanged while a keyboard dismissing/appearing animation is running. ### New features - Add `resizeToAvoidBottomInset` property to `SheetContentScaffold`. - Fixes #5. ### Documentation - Fixes #2. - Add a tutorial for `SheetKeyboardDismissBehavior`. - Add the description of `SheetKeyboardDismissBehavior` to the README.
- Loading branch information
Showing
28 changed files
with
1,867 additions
and
135 deletions.
There are no files selected for viewing
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
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
This file was deleted.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import 'package:cookbook/showcase/todo_list/models.dart'; | ||
import 'package:cookbook/showcase/todo_list/todo_editor.dart'; | ||
import 'package:flutter/material.dart'; | ||
|
||
void main() { | ||
runApp(const _TodoListExample()); | ||
} | ||
|
||
class _TodoListExample extends StatelessWidget { | ||
const _TodoListExample(); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return const MaterialApp(home: _Home()); | ||
} | ||
} | ||
|
||
class _Home extends StatefulWidget { | ||
const _Home(); | ||
|
||
@override | ||
State<_Home> createState() => _HomeState(); | ||
} | ||
|
||
class _HomeState extends State<_Home> { | ||
late final TodoList _todoList; | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
_todoList = TodoList(); | ||
} | ||
|
||
@override | ||
void dispose() { | ||
_todoList.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar( | ||
title: const Text('Todo List'), | ||
), | ||
body: _TodoListView(todoList: _todoList), | ||
floatingActionButton: FloatingActionButton( | ||
onPressed: () => addTodo(), | ||
child: const Icon(Icons.add), | ||
), | ||
); | ||
} | ||
|
||
Future<void> addTodo() async { | ||
final todo = await showTodoEditor(context); | ||
if (todo != null) { | ||
_todoList.add(todo); | ||
} | ||
} | ||
} | ||
|
||
class _TodoListView extends StatelessWidget { | ||
const _TodoListView({ | ||
required this.todoList, | ||
}); | ||
|
||
final TodoList todoList; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return ListenableBuilder( | ||
listenable: todoList, | ||
builder: (context, _) { | ||
return ListView.separated( | ||
itemCount: todoList.length, | ||
itemBuilder: (context, index) { | ||
return _TodoListViewItem( | ||
todo: todoList[index], | ||
checkboxCallback: (value) => todoList.toggle(index), | ||
); | ||
}, | ||
separatorBuilder: (context, index) { | ||
return const Divider(indent: 24); | ||
}, | ||
); | ||
}, | ||
); | ||
} | ||
} | ||
|
||
class _TodoListViewItem extends StatelessWidget { | ||
const _TodoListViewItem({ | ||
Key? key, | ||
required this.todo, | ||
required this.checkboxCallback, | ||
}) : super(key: key); | ||
|
||
final Todo todo; | ||
final ValueChanged<bool?> checkboxCallback; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final statuses = <Widget>[ | ||
if (todo.priority != Priority.none) | ||
_StatusChip( | ||
icon: Icons.flag, | ||
color: todo.priority.color, | ||
label: todo.priority.displayName, | ||
), | ||
]; | ||
|
||
final description = switch (todo.description) { | ||
null => null, | ||
final text => Text( | ||
text, | ||
maxLines: 2, | ||
overflow: TextOverflow.ellipsis, | ||
style: Theme.of(context) | ||
.textTheme | ||
.bodyLarge | ||
?.copyWith(color: Colors.black54), | ||
), | ||
}; | ||
|
||
final secondaryContent = [ | ||
if (description != null) ...[description, const SizedBox(height: 8)], | ||
if (statuses.isNotEmpty && !todo.isDone) Wrap(children: statuses), | ||
]; | ||
|
||
return CheckboxListTile( | ||
value: todo.isDone, | ||
controlAffinity: ListTileControlAffinity.leading, | ||
onChanged: checkboxCallback, | ||
title: Text(todo.title), | ||
isThreeLine: secondaryContent.isNotEmpty, | ||
subtitle: switch (secondaryContent.isNotEmpty) { | ||
true => Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: secondaryContent, | ||
), | ||
false => null, | ||
}, | ||
); | ||
} | ||
} | ||
|
||
class _StatusChip extends StatelessWidget { | ||
const _StatusChip({ | ||
required this.icon, | ||
required this.color, | ||
required this.label, | ||
}); | ||
|
||
final IconData icon; | ||
final Color? color; | ||
final String label; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Chip( | ||
avatar: Icon(icon, color: color), | ||
label: Text(label), | ||
padding: EdgeInsets.zero, | ||
labelPadding: const EdgeInsets.only(right: 12), | ||
visualDensity: const VisualDensity( | ||
vertical: -4, | ||
horizontal: -4, | ||
), | ||
); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import 'package:flutter/material.dart'; | ||
|
||
// TODO: Add 'reminder' and 'due date' fields. | ||
class Todo { | ||
final String title; | ||
final String? description; | ||
final Priority priority; | ||
final bool isDone; | ||
|
||
const Todo({ | ||
required this.title, | ||
this.description, | ||
this.priority = Priority.none, | ||
this.isDone = false, | ||
}); | ||
} | ||
|
||
enum Priority { | ||
high(displayName: 'High Priority', color: Colors.red), | ||
medium(displayName: 'Medium Priority', color: Colors.orange), | ||
low(displayName: 'Low Priority', color: Colors.blue), | ||
none(displayName: 'No Priority'); | ||
|
||
const Priority({ | ||
required this.displayName, | ||
this.color, | ||
}); | ||
|
||
final String displayName; | ||
final Color? color; | ||
} | ||
|
||
class TodoList extends ChangeNotifier { | ||
final List<Todo> _todos = []; | ||
|
||
int get length => _todos.length; | ||
|
||
Todo operator [](int index) => _todos[index]; | ||
|
||
void add(Todo todo) { | ||
_todos.insert(0, todo); | ||
notifyListeners(); | ||
} | ||
|
||
void toggle(int index) { | ||
final todo = _todos[index]; | ||
_todos[index] = Todo( | ||
title: todo.title, | ||
description: todo.description, | ||
priority: todo.priority, | ||
isDone: !todo.isDone, | ||
); | ||
notifyListeners(); | ||
} | ||
} |
Oops, something went wrong.