Migrate to null safety

This commit is contained in:
David Peters 2021-03-06 15:26:00 +01:00
commit b44039f9b6
9 changed files with 87 additions and 85 deletions

View File

@ -3,7 +3,7 @@ import 'screens/audio_screen.dart';
import 'widgets/player.dart';
import 'utils.dart';
ValueNotifier<AudioObject> currentlyPlaying = ValueNotifier(null);
ValueNotifier<AudioObject?> currentlyPlaying = ValueNotifier(null);
const double playerMinHeight = 70;
const double playerMaxHeight = 370;
@ -48,30 +48,20 @@ class _MyHomePageState extends State<MyHomePage> {
),
ValueListenableBuilder(
valueListenable: currentlyPlaying,
builder:
(BuildContext context, AudioObject audioObject, Widget child) =>
audioObject != null
? DetailedPlayer(audioObject: audioObject)
: Container(),
builder: (BuildContext context, AudioObject? audioObject,
Widget? child) =>
audioObject != null
? DetailedPlayer(audioObject: audioObject)
: Container(),
),
],
),
bottomNavigationBar: ValueListenableBuilder(
valueListenable: playerExpandProgress,
child: BottomNavigationBar(
currentIndex: 0,
selectedItemColor: Colors.blue,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Feed'),
BottomNavigationBarItem(
icon: Icon(Icons.library_books), label: 'Library'),
],
),
builder: (BuildContext context, double height, Widget child) {
builder: (BuildContext context, double height, Widget? child) {
final value = percentageFromValueInRange(
min: playerMinHeight, max: playerMaxHeight, value: height);
if (value == null) return child;
var opacity = 1 - value;
if (opacity < 0) opacity = 0;
if (opacity > 1) opacity = 1;
@ -85,6 +75,15 @@ class _MyHomePageState extends State<MyHomePage> {
),
);
},
child: BottomNavigationBar(
currentIndex: 0,
selectedItemColor: Colors.blue,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Feed'),
BottomNavigationBarItem(
icon: Icon(Icons.library_books), label: 'Library'),
],
),
),
);
}

View File

@ -27,7 +27,7 @@ const Set<AudioObject> audioExamples = {
class AudioUi extends StatelessWidget {
final OnTap onTap;
const AudioUi({Key key, @required this.onTap}) : super(key: key);
const AudioUi({Key? key, required this.onTap}) : super(key: key);
@override
Widget build(BuildContext context) {

View File

@ -1,5 +1,3 @@
import 'package:flutter/foundation.dart';
class AudioObject {
final String title, subtitle, img;
@ -7,10 +5,10 @@ class AudioObject {
}
double valueFromPercentageInRange(
{@required final double min, max, percentage}) {
{required final double min, max, percentage}) {
return percentage * (max - min) + min;
}
double percentageFromValueInRange({@required final double min, max, value}) {
double percentageFromValueInRange({required final double min, max, value}) {
return (value - min) / (max - min);
}

View File

@ -9,7 +9,7 @@ class AudioListTile extends StatelessWidget {
final Function onTap;
const AudioListTile(
{Key key, @required this.audioObject, @required this.onTap})
{Key? key, required this.audioObject, required this.onTap})
: super(key: key);
@override
@ -32,7 +32,7 @@ class AudioListTile extends StatelessWidget {
),
trailing: IconButton(
icon: Icon(Icons.play_arrow_outlined),
onPressed: onTap,
onPressed: () => onTap(),
),
);
}

View File

@ -12,7 +12,7 @@ final MiniplayerController controller = MiniplayerController();
class DetailedPlayer extends StatelessWidget {
final AudioObject audioObject;
const DetailedPlayer({Key key, @required this.audioObject}) : super(key: key);
const DetailedPlayer({Key? key, required this.audioObject}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -150,13 +150,13 @@ class DetailedPlayer extends StatelessWidget {
Text(audioObject.title,
style: Theme.of(context)
.textTheme
.bodyText2
.bodyText2!
.copyWith(fontSize: 16)),
Text(
audioObject.subtitle,
style: Theme.of(context)
.textTheme
.bodyText2
.bodyText2!
.copyWith(
color: Colors.black.withOpacity(0.55)),
),

View File

@ -37,24 +37,24 @@ class Miniplayer extends StatefulWidget {
///Allows you to use a global ValueNotifier with the current progress.
///This can be used to hide the BottomNavigationBar.
final ValueNotifier<double> valueNotifier;
final ValueNotifier<double>? valueNotifier;
///Deprecated
@Deprecated(
"Migrate onDismiss to onDismissed as onDismiss will be used differently in a future version.")
final Function onDismiss;
final Function? onDismiss;
///If onDismissed is set, the miniplayer can be dismissed
final Function onDismissed;
final Function? onDismissed;
//Allows you to manually control the miniplayer in code
final MiniplayerController controller;
final MiniplayerController? controller;
const Miniplayer({
Key key,
@required this.minHeight,
@required this.maxHeight,
@required this.builder,
Key? key,
required this.minHeight,
required this.maxHeight,
required this.builder,
this.curve = Curves.easeOut,
this.elevation = 0,
this.backgroundColor = const Color(0x70000000),
@ -70,17 +70,17 @@ class Miniplayer extends StatefulWidget {
}
class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
ValueNotifier<double> heightNotifier;
late ValueNotifier<double> heightNotifier;
ValueNotifier<double> dragDownPercentage = ValueNotifier(0);
///Temporary variable as long as onDismiss is deprecated. Will be removed in a future version.
Function onDismissed;
Function? onDismissed;
///Current y position of drag gesture
double _dragHeight;
late double _dragHeight;
///Used to determine SnapPosition
double _startHeight;
late double _startHeight;
bool dismissed = false;
@ -91,19 +91,19 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
StreamController<double> _heightController =
StreamController<double>.broadcast();
AnimationController _animationController;
AnimationController? _animationController;
void _statusListener(AnimationStatus status) {
if (status == AnimationStatus.completed) _resetAnimationController();
}
void _resetAnimationController({Duration duration}) {
if (_animationController != null) _animationController.dispose();
void _resetAnimationController({Duration? duration}) {
if (_animationController != null) _animationController!.dispose();
_animationController = AnimationController(
vsync: this,
duration: duration == null ? widget.duration : duration,
duration: duration ?? widget.duration,
);
_animationController.addStatusListener(_statusListener);
_animationController!.addStatusListener(_statusListener);
animating = false;
}
@ -112,14 +112,14 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
if (widget.valueNotifier == null)
heightNotifier = ValueNotifier(widget.minHeight);
else
heightNotifier = widget.valueNotifier;
heightNotifier = widget.valueNotifier!;
_resetAnimationController();
_dragHeight = heightNotifier.value;
if (widget.controller != null)
widget.controller.addListener(controllerListener);
widget.controller!.addListener(controllerListener);
if (widget.onDismissed != null)
onDismissed = widget.onDismissed;
@ -133,10 +133,10 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
@override
void dispose() {
_heightController.close();
_animationController.dispose();
if (_animationController != null) _animationController!.dispose();
if (widget.controller != null)
widget.controller.removeListener(controllerListener);
widget.controller!.removeListener(controllerListener);
super.dispose();
}
@ -154,8 +154,9 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
return true;
},
child: ValueListenableBuilder(
builder: (BuildContext context, double value, Widget child) {
var _percentage = ((value - widget.minHeight)) /
valueListenable: heightNotifier,
builder: (BuildContext context, double height, Widget? _) {
var _percentage = ((height - widget.minHeight)) /
(widget.maxHeight - widget.minHeight);
return Stack(
@ -173,12 +174,13 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: value,
height: height,
child: GestureDetector(
child: ValueListenableBuilder(
valueListenable: dragDownPercentage,
builder: (context, value, child) {
if (value == 0) return child;
builder:
(BuildContext context, double value, Widget? child) {
if (value == 0) return child!;
return Opacity(
opacity: borderDouble(
@ -193,7 +195,7 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
color: Theme.of(context).canvasColor,
child: Container(
constraints: BoxConstraints.expand(),
child: widget.builder(value, _percentage),
child: widget.builder(height, _percentage),
decoration: BoxDecoration(
boxShadow: <BoxShadow>[
BoxShadow(
@ -278,7 +280,6 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
],
);
},
valueListenable: heightNotifier,
),
);
}
@ -306,7 +307,7 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
dragDownPercentage.value = percentageDown;
if (percentageDown >= 1 && animation && !dismissed) {
if (onDismissed != null) onDismissed();
if (onDismissed != null) onDismissed!();
setState(() {
dismissed = true;
});
@ -330,7 +331,8 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
}
///Animates the panel height to a specific value
void _animateToHeight(final double h, {Duration duration}) {
void _animateToHeight(final double h, {Duration? duration}) {
if (_animationController == null) return;
final startHeight = _dragHeight;
if (duration != null) _resetAnimationController(duration: duration);
@ -339,7 +341,7 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
begin: startHeight,
end: h,
).animate(
CurvedAnimation(parent: _animationController, curve: widget.curve));
CurvedAnimation(parent: _animationController!, curve: widget.curve));
_sizeAnimation.addListener(() {
if (_sizeAnimation.value == startHeight) return;
@ -350,34 +352,37 @@ class _MiniplayerState extends State<Miniplayer> with TickerProviderStateMixin {
});
animating = true;
_animationController.forward(from: 0);
_animationController!.forward(from: 0);
}
//Listener function for the controller
void controllerListener() {
switch (widget.controller.value.height) {
if (widget.controller == null) return;
if (widget.controller!.value == null) return;
switch (widget.controller!.value!.height) {
case -1:
_animateToHeight(
widget.minHeight,
duration: widget.controller.value.duration,
duration: widget.controller!.value!.duration,
);
break;
case -2:
_animateToHeight(
widget.maxHeight,
duration: widget.controller.value.duration,
duration: widget.controller!.value!.duration,
);
break;
case -3:
_animateToHeight(
0,
duration: widget.controller.value.duration,
duration: widget.controller!.value!.duration,
);
break;
default:
_animateToHeight(
widget.controller.value.height.toDouble(),
duration: widget.controller.value.duration,
widget.controller!.value!.height.toDouble(),
duration: widget.controller!.value!.duration,
);
break;
}
@ -390,29 +395,30 @@ enum PanelState { MAX, MIN, DISMISS }
//ControllerData class. Used for the controller
class ControllerData {
final int height;
final Duration duration;
final Duration? duration;
const ControllerData(this.height, this.duration);
}
//MiniplayerController class
class MiniplayerController extends ValueNotifier<ControllerData> {
class MiniplayerController extends ValueNotifier<ControllerData?> {
MiniplayerController() : super(null);
//Animates to a given height or state(expanded, dismissed, ...)
void animateToHeight({double height, PanelState state, Duration duration}) {
void animateToHeight(
{double? height, PanelState? state, Duration? duration}) {
if (height == null && state == null)
throw ("Miniplayer: One of the two parameters, height or status, is required.");
if (height != null && state != null)
throw ("Miniplayer: Only one of the two parameters, height or status, can be specified.");
ControllerData valBefore = value;
ControllerData? valBefore = value;
if (state != null)
value = ControllerData(state.heightCode, duration);
else {
if (height < 0) return;
if (height! < 0) return;
value = ControllerData(height.round(), duration);
}

View File

@ -2,11 +2,10 @@ import 'package:flutter/material.dart';
class MiniplayerWillPopScope extends StatefulWidget {
const MiniplayerWillPopScope({
Key key,
@required this.child,
@required this.onWillPop,
}) : assert(child != null),
super(key: key);
Key? key,
required this.child,
required this.onWillPop,
}) : super(key: key);
final Widget child;
final WillPopCallback onWillPop;
@ -14,15 +13,15 @@ class MiniplayerWillPopScope extends StatefulWidget {
@override
_MiniplayerWillPopScopeState createState() => _MiniplayerWillPopScopeState();
static _MiniplayerWillPopScopeState of(BuildContext context) {
static _MiniplayerWillPopScopeState? of(BuildContext context) {
return context.findAncestorStateOfType<_MiniplayerWillPopScopeState>();
}
}
class _MiniplayerWillPopScopeState extends State<MiniplayerWillPopScope> {
ModalRoute<dynamic> _route;
ModalRoute<dynamic>? _route;
_MiniplayerWillPopScopeState _descendant;
_MiniplayerWillPopScopeState? _descendant;
set descendant(state) {
_descendant = state;
@ -30,11 +29,11 @@ class _MiniplayerWillPopScopeState extends State<MiniplayerWillPopScope> {
}
Future<bool> onWillPop() async {
bool willPop;
bool? willPop;
if (_descendant != null) {
willPop = await _descendant.onWillPop();
willPop = await _descendant!.onWillPop();
}
if (willPop == null || willPop) {
if (willPop == null) {
willPop = await widget.onWillPop();
}
return willPop;

View File

@ -16,11 +16,11 @@ extension SelectedColorExtension on PanelState {
}
///Calculates the percentage of a value within a given range of values
double percentageFromValueInRange({final double min, max, value}) {
double percentageFromValueInRange({required double min, max, value}) {
return (value - min) / (max - min);
}
double borderDouble({double minRange, double maxRange, double value}) {
double borderDouble({required double minRange, maxRange, value}) {
if (value > maxRange) return maxRange;
if (value < minRange) return minRange;
return value;

View File

@ -5,8 +5,8 @@ homepage: https://www.peterscode.dev
repository: https://github.com/peterscodee/miniplayer
environment:
sdk: ">=2.6.0 <3.0.0"
flutter: ">=1.0.0 <2.0.0"
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.0.1 <3.0.0"
dependencies:
flutter: