我正在制作一个音乐应用程序,并希望当前播放状态显示在大多数页面的底部。我通过bottomNavigationBar: const NowPlayingBar()在每个需要该杆的脚手架上使用来完成此操作。这有 2 个问题:
我发现执行此操作的唯一真正方法是使用 permanent_bottom_nav_bar 包,但这似乎不允许自定义小部件(有这个但NavBarStyle.custom似乎不存在)。有没有办法在所有页面上不断显示该栏?
这是显示问题 2 的视频:https://streamable.com/gxcswk
这是我现在正在播放的栏小部件(它基本上只是一个监听更改的列表图块):
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import '../components/AlbumImage.dart';
import '../services/mediaStateStream.dart';
import '../services/FinampSettingsHelper.dart';
import '../services/processArtist.dart';
import '../services/MusicPlayerBackgroundTask.dart';
class NowPlayingBar extends StatelessWidget {
const NowPlayingBar({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// BottomNavBar's default elevation is 8 (https://api.flutter.dev/flutter/material/BottomNavigationBar/elevation.html)
const elevation = 8.0;
final color = Theme.of(context).bottomNavigationBarTheme.backgroundColor;
final audioHandler = GetIt.instance<MusicPlayerBackgroundTask>();
return Material(
color: color,
elevation: elevation,
child: SafeArea(
child: StreamBuilder<MediaState>(
stream: mediaStateStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final playing = snapshot.data!.playbackState.playing;
// If we have a media item and the player hasn't finished, show
// the now playing bar.
if (snapshot.data!.mediaItem != null) {
return SizedBox(
width: MediaQuery.of(context).size.width,
child: Dismissible(
key: const Key("NowPlayingBar"),
confirmDismiss: (direction) async {
if (direction == DismissDirection.endToStart) {
audioHandler.skipToNext();
} else {
audioHandler.skipToPrevious();
}
return false;
},
background: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
AspectRatio(
aspectRatio: 1,
child: FittedBox(
fit: BoxFit.fitHeight,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Icon(Icons.skip_previous),
),
),
),
AspectRatio(
aspectRatio: 1,
child: FittedBox(
fit: BoxFit.fitHeight,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Icon(Icons.skip_next),
),
),
),
],
),
),
child: ListTile(
onTap: () =>
Navigator.of(context).pushNamed("/nowplaying"),
// We put the album image in a ValueListenableBuilder so that it reacts to offline changes
leading: ValueListenableBuilder(
valueListenable:
FinampSettingsHelper.finampSettingsListener,
builder: (context, _, widget) => AlbumImage(
itemId: snapshot.data!.mediaItem!.extras!["parentId"],
),
),
title: Text(
snapshot.data!.mediaItem!.title,
softWrap: false,
maxLines: 1,
overflow: TextOverflow.fade,
),
subtitle: Text(
processArtist(snapshot.data!.mediaItem!.artist),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (snapshot.data!.playbackState.processingState !=
AudioProcessingState.idle)
IconButton(
// We have a key here because otherwise the
// InkWell moves over to the play/pause button
key: const ValueKey("StopButton"),
icon: const Icon(Icons.stop),
onPressed: () => audioHandler.stop(),
),
playing
? IconButton(
icon: const Icon(Icons.pause),
onPressed: () => audioHandler.pause(),
)
: IconButton(
icon: const Icon(Icons.play_arrow),
onPressed: () => audioHandler.play(),
),
],
),
),
),
);
} else {
return const SizedBox(
width: 0,
height: 0,
);
}
} else {
return const SizedBox(
width: 0,
height: 0,
);
}
},
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
您可以做的是构建另一个导航器,其中包含正在播放的栏,如下所示:
(这是针对您的用例的该解决方案的简化版本)
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: VisiblePage(),
);
}
}
class VisiblePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
/* You could also make the body of the scaffold a stack, column,
etc. that has the the buildNavigator and the now playing bar as
its children instead of setting the NowPlayingBar as the bottom
nav bar and the buildNavigator as the body. */
return Scaffold(
backgroundColor: Colors.white,
body: _buildNavigator(context),
bottomNavigationBar: NowPlayingBar(),
);
}
Map<String, WidgetBuilder> _routeBuilders(BuildContext context, Map args) {
return {
"/": (context) {
return MainPage();
},
'/page1': (context) {
return Page1();
},
'/page2': (context) {
return Page2();
}
};
}
Widget _buildNavigator(BuildContext context) {
return Navigator(
onGenerateRoute: (settings) {
final args = settings.arguments ?? {};
var routeBuilders = _routeBuilders(context, args as Map);
return MaterialPageRoute(
fullscreenDialog: true,
settings: settings,
builder: (context) {
return routeBuilders[settings.name]!(context);
},
);
},
);
}
}
Run Code Online (Sandbox Code Playgroud)
要导航到第 1 页,只需使用:
Navigator.of(context).pushNamed('/page1');
对于第 2 页(不包含栏),将 rootNavigator 设置为 true:
Navigator.of(context, rootNavigator: true)
.push(MaterialPageRoute(builder: (context) => Page2()));
Run Code Online (Sandbox Code Playgroud)
这将使用“MyApp”页面中的初始导航器。您还可以通过将命名路由添加到“MyApp”页面来使其成为命名路由。
| 归档时间: |
|
| 查看次数: |
1525 次 |
| 最近记录: |