mirror of
https://github.com/flutter/samples.git
synced 2026-04-05 03:01:19 +00:00
Migrate web dashboard to null safety (#928)
* update dependencies * Update to cloud_firestore 2.x.x * run dart migrate * Fix analyzer warnings from null safety migration * Fix errors resulting from null safety migration * Fix info level warnings * run flutter format * fix tests * remove unused import, format
This commit is contained in:
@@ -11,12 +11,12 @@ import '../api/api.dart';
|
||||
/// one.
|
||||
class CategoryDropdown extends StatefulWidget {
|
||||
final CategoryApi api;
|
||||
final ValueChanged<Category> onSelected;
|
||||
final ValueChanged<Category?> onSelected;
|
||||
|
||||
const CategoryDropdown({
|
||||
@required this.api,
|
||||
@required this.onSelected,
|
||||
Key key,
|
||||
required this.api,
|
||||
required this.onSelected,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -24,9 +24,9 @@ class CategoryDropdown extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CategoryDropdownState extends State<CategoryDropdown> {
|
||||
Category _selected;
|
||||
Future<List<Category>> _future;
|
||||
Stream<List<Category>> _stream;
|
||||
Category? _selected;
|
||||
Future<List<Category>>? _future;
|
||||
Stream<List<Category>>? _stream;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -78,7 +78,7 @@ class _CategoryDropdownState extends State<CategoryDropdown> {
|
||||
initialData: futureSnapshot.hasData ? futureSnapshot.data : [],
|
||||
stream: _stream,
|
||||
builder: (context, snapshot) {
|
||||
var data = snapshot.hasData ? snapshot.data : <Category>[];
|
||||
var data = snapshot.hasData ? snapshot.data! : <Category>[];
|
||||
return DropdownButton<Category>(
|
||||
value: _selected,
|
||||
items: data.map(_buildDropdownItem).toList(),
|
||||
@@ -92,7 +92,7 @@ class _CategoryDropdownState extends State<CategoryDropdown> {
|
||||
);
|
||||
}
|
||||
|
||||
void _setSelected(Category category) {
|
||||
void _setSelected(Category? category) {
|
||||
if (_selected == category) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ const _daysBefore = 10;
|
||||
|
||||
class CategoryChart extends StatelessWidget {
|
||||
final Category category;
|
||||
final DashboardApi api;
|
||||
final DashboardApi? api;
|
||||
|
||||
const CategoryChart({
|
||||
@required this.category,
|
||||
@required this.api,
|
||||
Key key,
|
||||
required this.category,
|
||||
required this.api,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -51,14 +51,14 @@ class CategoryChart extends StatelessWidget {
|
||||
// Load the initial snapshot using a FutureBuilder, and subscribe to
|
||||
// additional updates with a StreamBuilder.
|
||||
child: FutureBuilder<List<Entry>>(
|
||||
future: api.entries.list(category.id),
|
||||
future: api!.entries.list(category.id!),
|
||||
builder: (context, futureSnapshot) {
|
||||
if (!futureSnapshot.hasData) {
|
||||
return _buildLoadingIndicator();
|
||||
}
|
||||
return StreamBuilder<List<Entry>>(
|
||||
initialData: futureSnapshot.data,
|
||||
stream: api.entries.subscribe(category.id),
|
||||
stream: api!.entries.subscribe(category.id!),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return _buildLoadingIndicator();
|
||||
@@ -74,12 +74,14 @@ class CategoryChart extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _buildLoadingIndicator() {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _BarChart extends StatelessWidget {
|
||||
final List<Entry> entries;
|
||||
final List<Entry>? entries;
|
||||
|
||||
const _BarChart({this.entries});
|
||||
|
||||
@@ -96,14 +98,10 @@ class _BarChart extends StatelessWidget {
|
||||
id: 'Entries',
|
||||
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||
domainFn: (entryTotal, _) {
|
||||
if (entryTotal == null) return null;
|
||||
|
||||
var format = intl.DateFormat.Md();
|
||||
return format.format(entryTotal.day);
|
||||
},
|
||||
measureFn: (total, _) {
|
||||
if (total == null) return null;
|
||||
|
||||
return total.value;
|
||||
},
|
||||
data: utils.entryTotalsByDay(entries, _daysBefore),
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:web_dashboard/src/api/api.dart';
|
||||
import 'package:web_dashboard/src/app.dart';
|
||||
|
||||
class NewCategoryForm extends StatefulWidget {
|
||||
const NewCategoryForm({Key key}) : super(key: key);
|
||||
const NewCategoryForm({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_NewCategoryFormState createState() => _NewCategoryFormState();
|
||||
@@ -24,7 +24,7 @@ class _NewCategoryFormState extends State<NewCategoryForm> {
|
||||
category: _category,
|
||||
onDone: (shouldInsert) {
|
||||
if (shouldInsert) {
|
||||
api.categories.insert(_category);
|
||||
api!.categories.insert(_category);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
@@ -37,9 +37,9 @@ class EditCategoryForm extends StatefulWidget {
|
||||
final ValueChanged<bool> onDone;
|
||||
|
||||
const EditCategoryForm({
|
||||
@required this.category,
|
||||
@required this.onDone,
|
||||
Key key,
|
||||
required this.category,
|
||||
required this.onDone,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -67,7 +67,7 @@ class _EditCategoryFormState extends State<EditCategoryForm> {
|
||||
widget.category.name = newValue;
|
||||
},
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
if (value!.isEmpty) {
|
||||
return 'Please enter a name';
|
||||
}
|
||||
return null;
|
||||
@@ -91,7 +91,7 @@ class _EditCategoryFormState extends State<EditCategoryForm> {
|
||||
child: ElevatedButton(
|
||||
child: const Text('OK'),
|
||||
onPressed: () {
|
||||
if (_formKey.currentState.validate()) {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
widget.onDone(true);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import '../app.dart';
|
||||
import 'edit_entry.dart';
|
||||
|
||||
class NewCategoryDialog extends StatelessWidget {
|
||||
const NewCategoryDialog({Key key}) : super(key: key);
|
||||
const NewCategoryDialog({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -28,8 +28,8 @@ class EditCategoryDialog extends StatelessWidget {
|
||||
final Category category;
|
||||
|
||||
const EditCategoryDialog({
|
||||
@required this.category,
|
||||
Key key,
|
||||
required this.category,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -43,7 +43,7 @@ class EditCategoryDialog extends StatelessWidget {
|
||||
category: category,
|
||||
onDone: (shouldUpdate) {
|
||||
if (shouldUpdate) {
|
||||
api.categories.update(category, category.id);
|
||||
api!.categories.update(category, category.id!);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
@@ -54,7 +54,7 @@ class EditCategoryDialog extends StatelessWidget {
|
||||
}
|
||||
|
||||
class NewEntryDialog extends StatefulWidget {
|
||||
const NewEntryDialog({Key key}) : super(key: key);
|
||||
const NewEntryDialog({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_NewEntryDialogState createState() => _NewEntryDialogState();
|
||||
@@ -73,13 +73,13 @@ class _NewEntryDialogState extends State<NewEntryDialog> {
|
||||
}
|
||||
|
||||
class EditEntryDialog extends StatelessWidget {
|
||||
final Category category;
|
||||
final Entry entry;
|
||||
final Category? category;
|
||||
final Entry? entry;
|
||||
|
||||
const EditEntryDialog({
|
||||
this.category,
|
||||
this.entry,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -93,7 +93,7 @@ class EditEntryDialog extends StatelessWidget {
|
||||
entry: entry,
|
||||
onDone: (shouldUpdate) {
|
||||
if (shouldUpdate) {
|
||||
api.entries.update(category.id, entry.id, entry);
|
||||
api!.entries.update(category!.id!, entry!.id!, entry!);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
||||
@@ -11,19 +11,19 @@ import '../app.dart';
|
||||
import 'categories_dropdown.dart';
|
||||
|
||||
class NewEntryForm extends StatefulWidget {
|
||||
const NewEntryForm({Key key}) : super(key: key);
|
||||
const NewEntryForm({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_NewEntryFormState createState() => _NewEntryFormState();
|
||||
}
|
||||
|
||||
class _NewEntryFormState extends State<NewEntryForm> {
|
||||
Category _selected;
|
||||
late Category _selected;
|
||||
final Entry _entry = Entry(0, DateTime.now());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var api = Provider.of<AppState>(context).api;
|
||||
var api = Provider.of<AppState>(context).api!;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -33,6 +33,7 @@ class _NewEntryFormState extends State<NewEntryForm> {
|
||||
child: CategoryDropdown(
|
||||
api: api.categories,
|
||||
onSelected: (category) {
|
||||
if (category == null) return;
|
||||
setState(() {
|
||||
_selected = category;
|
||||
});
|
||||
@@ -43,7 +44,7 @@ class _NewEntryFormState extends State<NewEntryForm> {
|
||||
entry: _entry,
|
||||
onDone: (shouldInsert) {
|
||||
if (shouldInsert) {
|
||||
api.entries.insert(_selected.id, _entry);
|
||||
api.entries.insert(_selected.id!, _entry);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
@@ -54,13 +55,13 @@ class _NewEntryFormState extends State<NewEntryForm> {
|
||||
}
|
||||
|
||||
class EditEntryForm extends StatefulWidget {
|
||||
final Entry entry;
|
||||
final Entry? entry;
|
||||
final ValueChanged<bool> onDone;
|
||||
|
||||
const EditEntryForm({
|
||||
@required this.entry,
|
||||
@required this.onDone,
|
||||
Key key,
|
||||
required this.entry,
|
||||
required this.onDone,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -80,19 +81,19 @@ class _EditEntryFormState extends State<EditEntryForm> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: TextFormField(
|
||||
initialValue: widget.entry.value.toString(),
|
||||
initialValue: widget.entry!.value.toString(),
|
||||
decoration: const InputDecoration(labelText: 'Value'),
|
||||
keyboardType: TextInputType.number,
|
||||
validator: (value) {
|
||||
try {
|
||||
int.parse(value);
|
||||
int.parse(value!);
|
||||
} catch (e) {
|
||||
return "Please enter a whole number";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onChanged: (newValue) {
|
||||
widget.entry.value = int.parse(newValue);
|
||||
widget.entry!.value = int.parse(newValue);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -101,13 +102,13 @@ class _EditEntryFormState extends State<EditEntryForm> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(intl.DateFormat('MM/dd/yyyy').format(widget.entry.time)),
|
||||
Text(intl.DateFormat('MM/dd/yyyy').format(widget.entry!.time)),
|
||||
ElevatedButton(
|
||||
child: const Text('Edit'),
|
||||
onPressed: () async {
|
||||
var result = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: widget.entry.time,
|
||||
initialDate: widget.entry!.time,
|
||||
firstDate:
|
||||
DateTime.now().subtract(const Duration(days: 365)),
|
||||
lastDate: DateTime.now());
|
||||
@@ -115,7 +116,7 @@ class _EditEntryFormState extends State<EditEntryForm> {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
widget.entry.time = result;
|
||||
widget.entry!.time = result;
|
||||
});
|
||||
},
|
||||
)
|
||||
@@ -139,7 +140,7 @@ class _EditEntryFormState extends State<EditEntryForm> {
|
||||
child: ElevatedButton(
|
||||
child: const Text('OK'),
|
||||
onPressed: () {
|
||||
if (_formKey.currentState.validate()) {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
widget.onDone(true);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,8 +18,8 @@ class AdaptiveScaffoldDestination {
|
||||
final IconData icon;
|
||||
|
||||
const AdaptiveScaffoldDestination({
|
||||
@required this.title,
|
||||
@required this.icon,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,23 +27,23 @@ class AdaptiveScaffoldDestination {
|
||||
/// [NavigationRail], or [BottomNavigationBar]. Navigation destinations are
|
||||
/// defined in the [destinations] parameter.
|
||||
class AdaptiveScaffold extends StatefulWidget {
|
||||
final Widget title;
|
||||
final Widget? title;
|
||||
final List<Widget> actions;
|
||||
final Widget body;
|
||||
final Widget? body;
|
||||
final int currentIndex;
|
||||
final List<AdaptiveScaffoldDestination> destinations;
|
||||
final ValueChanged<int> onNavigationIndexChange;
|
||||
final FloatingActionButton floatingActionButton;
|
||||
final ValueChanged<int>? onNavigationIndexChange;
|
||||
final FloatingActionButton? floatingActionButton;
|
||||
|
||||
const AdaptiveScaffold({
|
||||
this.title,
|
||||
this.body,
|
||||
this.actions = const [],
|
||||
@required this.currentIndex,
|
||||
@required this.destinations,
|
||||
required this.currentIndex,
|
||||
required this.destinations,
|
||||
this.onNavigationIndexChange,
|
||||
this.floatingActionButton,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -122,7 +122,7 @@ class _AdaptiveScaffoldState extends State<AdaptiveScaffold> {
|
||||
color: Colors.grey[300],
|
||||
),
|
||||
Expanded(
|
||||
child: widget.body,
|
||||
child: widget.body!,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -155,7 +155,7 @@ class _AdaptiveScaffoldState extends State<AdaptiveScaffold> {
|
||||
void _destinationTapped(AdaptiveScaffoldDestination destination) {
|
||||
var idx = widget.destinations.indexOf(destination);
|
||||
if (idx != widget.currentIndex) {
|
||||
widget.onNavigationIndexChange(idx);
|
||||
widget.onNavigationIndexChange!(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user