
开篇寄语
这一篇文章主要是学习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]);
},
),
),
),
);
}
}
生成效果如下图所示:

- 我的微信
- 微信扫一扫加好友
-
- 我的微信公众号
- 扫描关注公众号
-





