开篇寄语
这一篇文章主要是学习flutter中放置各种媒体资源,放置各种影音文件,网络图片等等,当然在App中,表现的形式也是千变万化。
前情提要
- 《Flutter制作自己的第一个全平台APP学习之Widgets篇》
- 《Flutter制作自己的第一个全平台APP学习之Layouts篇》
- 《Flutter制作自己的第一个全平台APP学习之Lists篇》
- 《Flutter制作自己的第一个全平台APP学习之AppBar篇》
- 《Flutter制作自己的第一个全平台APP学习之Navigation篇》
内容详情
以下内容大多是更改lib下的main.dart文件内容,删掉里面的内容,复制粘贴代码,开启调试就可以出现了。
Video
- flutter应用有一个库是video player,支持很多种格式的多媒体文件,尤其是视频文件,从官方的指导范例的一例来看一下。
先要安装video player这个支撑库,在pubspec.yaml文件,添加库:
dependencies: video_player: ^2.1.6
接着添加android和ios视频调用权限,iOS的调试在模拟器中无法实现,只能真机查看效果,Android系统在android/app/src/main/中的AndroidManifest.xml文件添加以下代码<uses-permission android:name="android.permission.INTERNET"/>:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application ...> </application> <uses-permission android:name="android.permission.INTERNET"/> </manifest>
iOS系统在ios/Runner/中的Info.plist文件添加以下代码:
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
接着在main.dart文件输入以下代码:
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; void main() => runApp(VideoPlayerApp()); class VideoPlayerApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Video Player Demo', home: VideoPlayerScreen(), ); } } class VideoPlayerScreen extends StatefulWidget { VideoPlayerScreen({Key? key}) : super(key: key); @override _VideoPlayerScreenState createState() => _VideoPlayerScreenState(); } class _VideoPlayerScreenState extends State<VideoPlayerScreen> { late VideoPlayerController _controller; late Future<void> _initializeVideoPlayerFuture; @override void initState() { // Create and store the VideoPlayerController. The VideoPlayerController // offers several different constructors to play videos from assets, files, // or the internet. _controller = VideoPlayerController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4', ); // Initialize the controller and store the Future for later use. _initializeVideoPlayerFuture = _controller.initialize(); // Use the controller to loop the video. _controller.setLooping(true); super.initState(); } @override void dispose() { // Ensure disposing of the VideoPlayerController to free up resources. _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Butterfly Video'), ), // Use a FutureBuilder to display a loading spinner while waiting for the // VideoPlayerController to finish initializing. body: FutureBuilder( future: _initializeVideoPlayerFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { // If the VideoPlayerController has finished initialization, use // the data it provides to limit the aspect ratio of the video. return AspectRatio( aspectRatio: _controller.value.aspectRatio, // Use the VideoPlayer widget to display the video. child: VideoPlayer(_controller), ); } else { // If the VideoPlayerController is still initializing, show a // loading spinner. return Center(child: CircularProgressIndicator()); } }, ), floatingActionButton: FloatingActionButton( onPressed: () { // Wrap the play or pause in a call to `setState`. This ensures the // correct icon is shown. setState(() { // If the video is playing, pause it. if (_controller.value.isPlaying) { _controller.pause(); } else { // If the video is paused, play it. _controller.play(); } }); }, // Display the correct icon depending on the state of the player. child: Icon( _controller.value.isPlaying ? Icons.pause : Icons.play_arrow, ), ), ); } }
生成效果如下图所示:
music
- video player这个插件除了支持播放视频,还能支持播放音频,按照以上的方法添加支持依赖,增加权限命令后,main.dart文件中输入以下代码:
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; void main() => runApp(VideoPlayerApp()); class VideoPlayerApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Video Player Demo', home: VideoPlayerScreen(), ); } } class VideoPlayerScreen extends StatefulWidget { VideoPlayerScreen({Key? key}) : super(key: key); @override _VideoPlayerScreenState createState() => _VideoPlayerScreenState(); } class _VideoPlayerScreenState extends State<VideoPlayerScreen> { late VideoPlayerController _controller; late Future<void> _initializeVideoPlayerFuture; @override void initState() { // Using an mp3 file instead of mp4. _controller = VideoPlayerController.network( 'http://sp.9sky.com/convert/song/music/1027952/20210506105039040.mp3', ); _initializeVideoPlayerFuture = _controller.initialize(); _controller.setLooping(true); super.initState(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Butterfly Video'), ), body: FutureBuilder( future: _initializeVideoPlayerFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { // not wrapped in an AspectRatio widget return VideoPlayer(_controller); } else { return Center(child: CircularProgressIndicator()); } }, ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { if (_controller.value.isPlaying) { _controller.pause(); } else { _controller.play(); } }); }, child: Icon( _controller.value.isPlaying ? Icons.pause : Icons.play_arrow, ), ), ); } }
生成的效果未见播放内容,但是点击播放键可以播放音乐的声音,大家可以换上自己的自定义播放地址来测试。
网络地址加载图片
- 此前介绍过如何从本地加载图片,其实还可以加载网络地址中的图片,试举例如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { var title = 'Web Images'; return MaterialApp( title: title, home: Scaffold( appBar: AppBar( title: Text(title), ), body: Image.network('https://picsum.photos/250?image=9'), ), ); } }
生成的效果如下图所示:
带有视频控件的播放器
- video player虽是flutter的官方支持库,但是在表现上与一个名为chewie的插件相比稍逊风骚,在视频控制上的功能支持表现好得多,试举例如下:
首先是安装chewie的支撑库,在pubspec.yaml文件添加,前提是已经安装好video_player哦,代码如下:
dependencies: chewie: ^1.2.2
之后在lib文件夹下新建一个文件,命名为chewie_list_item.dart,输入以下代码:
import 'package:chewie/chewie.dart'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; class ChewieListItem extends StatefulWidget { // This will contain the URL/asset path which we want to play final VideoPlayerController videoPlayerController; final bool looping; ChewieListItem({ required this.videoPlayerController, this.looping = true, Key? key, }) : super(key: key); @override _ChewieListItemState createState() => _ChewieListItemState(); } class _ChewieListItemState extends State<ChewieListItem> { late ChewieController _chewieController; @override void initState() { super.initState(); // Wrapper on top of the videoPlayerController _chewieController = ChewieController( videoPlayerController: widget.videoPlayerController, aspectRatio: 16 / 9, // Prepare the video to be played and display the first frame autoInitialize: true, looping: widget.looping, // Errors can occur for example when trying to play a video // from a non-existent URL errorBuilder: (context, errorMessage) { return Center( child: Text( errorMessage, style: TextStyle(color: Colors.white), ), ); }, ); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), child: Chewie( controller: _chewieController, ), ); } @override void dispose() { super.dispose(); // IMPORTANT to dispose of all the used resources widget.videoPlayerController.dispose(); _chewieController.dispose(); } }
之后在main.dart文件内放置以下代码:
import 'chewie_list_item.dart'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Video Player'), ), body: ListView( children: <Widget>[ ChewieListItem( videoPlayerController: VideoPlayerController.network( 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4', ), ), ], ), ); } }
生成效果如下图所示:
带有加载动画的图片
- 加载图片前如果图片太大,可以放置前置加载动画以使得应用有连贯性,试举例如下:
可以前面放一个加载动图,这样也相对简单,从官方给出的示例就可以看到,代码如下:
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final title = 'Fade in images'; return MaterialApp( title: title, home: Scaffold( appBar: AppBar( title: Text(title), ), body: Center( child: FadeInImage.assetNetwork( placeholder: 'assets/loading.gif', image: 'https://picsum.photos/250?image=9', ), ), ), ); } }
生成的效果如下图所示:
有一个支持库,名为transparent_image,简单的转圈加载动画,但是效果却是很好,试举例如下:
首先是安装transparent_image这个库,可以从pub.dev这个网站找到它,安装完成后,在main.dart文件使用如下代码:
import 'package:flutter/material.dart'; import 'package:transparent_image/transparent_image.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final title = 'Fade in images'; return MaterialApp( title: title, home: Scaffold( appBar: AppBar( title: Text(title), ), body: Stack( children: <Widget>[ Center(child: CircularProgressIndicator()), Center( child: FadeInImage.memoryNetwork( placeholder: kTransparentImage, image: 'https://picsum.photos/250?image=9', ), ), ], ), ), ); } }
生成效果如下图所示:
可以看到会有一段加载动画,之后就能显示图片了。
圆形图片
- 装点美化图片的作用,而且实现的方法也很简单,试举例如下:
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( appBar: AppBar( title: Text('Flutter Buttons - FlatButton'), ), body: Center( child: ClipRRect( borderRadius: BorderRadius.circular(800), child: Image.network( 'https://cdn.pixabay.com/photo/2017/05/31/18/38/sea-2361247_960_720.jpg', width: 400, height: 400, fit: BoxFit.cover, ), ), ), ), ); } }
生成效果如下图所示:
在图片上带有标题
- 这个模式在日常编写App中也常常用到,试举例如下:
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( // Hide the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Flutter Demo"), ), body: Stack( children: [ Image.network( 'https://images.unsplash.com/photo-1444703686981-a3abbc4d4fe3?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8cGljfGVufDB8fDB8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', width: double.infinity, height: 250, fit: BoxFit.cover), Positioned( // The Positioned widget is used to position the text inside the Stack widget bottom: 10, right: 10, child: Container( // We use this Container to create a black box that wraps the white text so that the user can read the text even when the image is white width: 300, color: Colors.black54, padding: EdgeInsets.all(10), child: Text( 'I Like Potatoes And Oranges', style: TextStyle(fontSize: 20, color: Colors.white), ), ), ) ], )); } }
生成效果如下图所示:
除了使用stack,还可以使用SizedBox这个模块实现,试举例如下:
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( // Hide the debug banner debugShowCheckedModeBanner: false, title: 'Flutter Demo', home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Flutter Demo"), ), body: SizedBox( width: double.infinity, height: 250, child: GridTile( child: Image.network( 'https://cdn.pixabay.com/photo/2020/10/09/19/39/utah-5641320_960_720.jpg', width: double.infinity, height: 250, fit: BoxFit.cover), footer: Container( // You can use GridTileBar instead child: Text( 'This Is A Beautiful Photo', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold), textAlign: TextAlign.right, ), width: double.infinity, padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15), color: Colors.black54, ), ), ), ); } }
加载本地txt文档
- 本地有一个名为counter.txt文件,里面有一串演示的文案,要想加载本地的文档,需要提前在pubspec.yaml文件中添加引导路径,就如之前添加图片路径引导一样,示例如下:
assets: - assets/counter.txt
添加文件夹及文件在主目录中,参考如图:
接着在main.dart文件添加代码:
import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Questions', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyAppScreen(), ); } } class MyAppScreen extends StatefulWidget { @override State<StatefulWidget> createState() { return MyAppScreenState(); } } class MyAppScreenState extends State<MyAppScreen> { List<String> _questions = []; Future<List<String>> _loadQuestions() async { List<String> questions = []; await rootBundle.loadString('assets/counter.txt').then((q) { for (String i in LineSplitter().convert(q)) { questions.add(i); } }); return questions; } @override void initState() { _setup(); super.initState(); } _setup() async { // Retrieve the questions (Processed in the background) List<String> questions = await _loadQuestions(); // Notify the UI and display the questions setState(() { _questions = questions; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter Questions")), body: Center( child: Container( child: ListView.builder( itemCount: _questions.length, itemBuilder: (context, index) { return Text(_questions[index]); }, ), ), ), ); } }
生成效果如下图所示:
ArrayArrayArray- 我的微信
- 微信扫一扫加好友
- 我的微信公众号
- 扫描关注公众号