۱۰ پکیج برتر و محبوب فلاتر | فهرست کاربردی
فلاتر (Flutter) یک کیت ابزار UI ارائه شده از سوی گوگل است که برای ساخت اپلیکیشنهای زیبایی استفاده میشود که به صورت نیتیو از یک کدبیس منفرد برای موبایل، وب و دسکتاپ کامپایل میشوند. در این مقاله 10 پکیج برتر و محبوب فلاتر را در یک فهرست کاربردی ارائه میکنیم که ایدهای کلی در مورد میزان بلوغ فلاتر به عنوان یک پلتفرم ارائه میکنند.
فلاتر بر مینای زبان برنامهنویسی دارت (Dart) عمل میکند. این زبان یک جامعه بزرگ و زنده روی وبسایت Dart.dev دارد که هم پشتیبانی رسمی از این زبان انجام میدهند و هم پکیجهای شخص ثالث برای ایجاد بهرهوری بیشتر در فرایند توسعه فلاتر عرضه میکنند.
اگر هنوز با فلاتر آشنا نشدهاید، پیشنهاد میکنیم ابتدا با استفاده از مقالههای زیر در مورد فلاتر، ویژگیها و کاربردهای آن اطلاعات مقدماتی را به دست آورید:
HTTP
این پکیج (+) روی پلتفرمهای اندروید، iOS و وب کار میکند. این روزها هر چیزی مبتنی بر وب است و از این رو یک کتابخانه قوی HTTP چیزی است که هر کس به آن نیاز دارد. این پکیج دارت شامل یک مجموعه از تابعها و کلاسهای سطح بالا است که امکان مصرف منابع HTTP را تسهیل میکند. این پکیج به خوبی توسعه یافته است و به صورت فعالانهای از سوی تیم دارت نگهداری میشود. این پکیج از سال 2012 معرفی شده است و از این رو در حال حاضر به بلوغ کامل رسیده است.
کتابخانه HTTP دارت تابعهای سطح بالایی ارائه میکند که موجب میشود کار با HTTP آسان باشد:
1import 'package:http/http.dart' as http;
2# Posting data
3var url = 'https://example.com/whatsit/create';
4var data = {'name': 'Jack', 'age': 38};
5var response = await http.post(url, body: data);
6print('Response status: ${response.statusCode}');
7print('Response body: ${response.body}');
8# A simple GET request
9print(await http.read('https://example.com/foobar.txt'));
flutter_slidable
این پکیج فلاتر (+) روی پلتفرمهای اندروید، iOS و وب کار میکند.
پلاگین flutter_slidable یک ویجت اسلایدر با امکانات زیاد به پروژه شما اضافه میکند. اسلایدرهای مانند این غالباً در لیستهای قابل اسکرول دیده میشوند. اپلیکیشن جیمیل یک مثال مهم از این ویجت است و نشان میدهد که لیست اسلایدی یک بهینهسازی چشمگیر در زمینه بهرهوری استفاده از اپلیکیشن محسوب میشود.
این پلاگین مجهز به قابلیتهای زیاد و بیدرنگ قابل استفاده است، اما در صورت نیاز میتوان آن را تا حدود زیادی سفارشیسازی نیز کرد. برخی از قابلیتهای مهم آن به شرح زیر هستند:
- لیستهای ویجت اصلی (چپ/بالا) و ثانویه (راست/پایین) را به صورت اکشنهای گزینههای اسلاید میپذیرد.
- میتواند محو شود.
- دارای چهار پنل اکشن داخلی است.
- دو ویجت اکشن اسلاید داخلی دارد.
- انیمیشن داخلی برای محو شدن دارد.
- لیآوتها و انیمیشنهای سفارشی به آسانی تولید میشوند.
- زمانی که روی یک اکشن اسلاید ضربه بزنید، بسته خواهد شد (قابل override است).
- گزینهای آسان برای غیر فعالسازی افکت اسلاید دارد.
Shared Preferences
این پکیج (+) روی پلتفرمهای اندروید، iOS، وب و لینوکس عمل میکند. پکیج Shared Preferences کتابخانههای ذخیرهسازی دائمی خاص هر پلتفرم را درون خود جای داده است. به این ترتیب میتوان دادههای سادهای مانند ترجیحهای کاربر را ذخیره ساخت. کاربردهای آن به شرح زیر هستند:
- NSUserDefaults روی iOS و macOS
- SharedPreferences روی اندروید
- LocalStorage روی وبسایتها
- یک فایل JSON روی فایلسیستم لوکال برای لینوکس
دادهها ممکن است روی دیسک به صورت ناهمگام ذخیره شوند و هیچ تضمینی نیست که پس از بازگشت روی دیسک باقی مانده باشند، بنابراین این پلاگین به معنی ذخیره دادههای مهم نیست. به این منظور باید از sqflite استفاده کنید که در ادامه توضیح دادهایم.
Sqflite
این پکیج (+) روی پلتفرمهای اندروید، iOS و MacOS کار میکند. Sqflite یک پلاگین SQLite برای فلاتر است. این پکیج از پلتفرمهای مختلف پشتیبانی میکند، اما وب پشتیبانی نشده است چون هیچ سیستم ذخیرهسازی دائمی مبتنی بر SQL در مرورگرهای وب تعبیه نشده است. برخی از قابلیتهای آن به شرح زیر هستند:
- پشتیبانی از تراکنشها و batch-ها
- مدیریت خودکار نسخه
- تابعهای کمکی برای درج، اجرای کوئری، بهروزرسانی و حذف دادهها
- عملیات مختلف در نخ پسزمینه روی iOS و اندروید اجرا میشود تا UI را از قفل شدن بازدارد.
اگر به چیزی بیش از ذخیره ساده دادهها نیاز دارید، این بهترین گزینه است.
url_launcher
این پکیج (+) برای پلتفرمهای اندروید، iOS و وب ارائه شده است. این پلاگین به اجرای یک URL کمک میکند. URL-ها میتوانند به صورتهای زیر باشند:
- HTTP مانند http://example.org و https://example.org
- ایمیل مانند mailto:<e-mail address>
- شماره تلفن مانند tel:<phone number>
- متنهای پیامک مانند sms:<phone number>
کاربرد ابتدایی آن کاملاً سرراست است:
1const url = 'https://flutter.dev';
2if (await canLaunch(url)) {
3 await launch(url);
4} else {
5 throw 'Could not launch $url';
6}
video_player
این پکیج (+) برای پلتفرمهای اندروید، iOS و وب ارائه شده است.
پکیج video_player از فرمتهای مختلفی پشتیبانی میکند، اما همه آنها به پلتفرمی که اجرا میکنید، وابسته است. برای نمونه کتابخانههای پشتیبانی سیستمهای iOS و اندروید متفاوت هستند. همچنین روی وب فرمتهایی که پشتیبانی میشوند به مرورگری که استفاده میکنید، وابستهاند.
توجه کنید که گرچه این پکیج ویدئو پلیر نام دارد، اما این پلاگین میتواند صوت را نیز پخش کند. از آنجا که این پلاگین کاملاً به بلوغ رسیده است و API با ثباتی دارد، میتوانید از آن به جای برخی موارد دیگر، برای پخش صوت استفاده کنید.
این پلاگین میتواند ویدئو را از فایل لوکال (assets) سرور ریموت (مانند یک وبسایت) پخش کند. برای نمونه در قطعه کد زیر یک نمونه از روش استفاده از این پکیج را مشاهده میکنید.
1/// An example of using the plugin, controlling lifecycle and playback of the
2/// video.
3
4import 'package:flutter/cupertino.dart';
5import 'package:flutter/material.dart';
6import 'package:video_player/video_player.dart';
7
8void main() {
9 runApp(
10 MaterialApp(
11 home: _App(),
12 ),
13 );
14}
15
16class _App extends StatelessWidget {
17 @override
18 Widget build(BuildContext context) {
19 return DefaultTabController(
20 length: 3,
21 child: Scaffold(
22 key: const ValueKey<String>('home_page'),
23 appBar: AppBar(
24 title: const Text('Video player example'),
25 actions: <Widget>[
26 IconButton(
27 key: const ValueKey<String>('push_tab'),
28 icon: const Icon(Icons.navigation),
29 onPressed: () {
30 Navigator.push<_PlayerVideoAndPopPage>(
31 context,
32 MaterialPageRoute<_PlayerVideoAndPopPage>(
33 builder: (BuildContext context) => _PlayerVideoAndPopPage(),
34 ),
35 );
36 },
37 )
38 ],
39 bottom: const TabBar(
40 isScrollable: true,
41 tabs: <Widget>[
42 Tab(
43 icon: Icon(Icons.cloud),
44 text: "Remote",
45 ),
46 Tab(icon: Icon(Icons.insert_drive_file), text: "Asset"),
47 Tab(icon: Icon(Icons.list), text: "List example"),
48 ],
49 ),
50 ),
51 body: TabBarView(
52 children: <Widget>[
53 _BumbleBeeRemoteVideo(),
54 _ButterFlyAssetVideo(),
55 _ButterFlyAssetVideoInList(),
56 ],
57 ),
58 ),
59 );
60 }
61}
62
63class _ButterFlyAssetVideoInList extends StatelessWidget {
64 @override
65 Widget build(BuildContext context) {
66 return ListView(
67 children: <Widget>[
68 _ExampleCard(title: "Item a"),
69 _ExampleCard(title: "Item b"),
70 _ExampleCard(title: "Item c"),
71 _ExampleCard(title: "Item d"),
72 _ExampleCard(title: "Item e"),
73 _ExampleCard(title: "Item f"),
74 _ExampleCard(title: "Item g"),
75 Card(
76 child: Column(children: <Widget>[
77 Column(
78 children: <Widget>[
79 const ListTile(
80 leading: Icon(Icons.cake),
81 title: Text("Video video"),
82 ),
83 Stack(
84 alignment: FractionalOffset.bottomRight +
85 const FractionalOffset(-0.1, -0.1),
86 children: <Widget>[
87 _ButterFlyAssetVideo(),
88 Image.asset('assets/flutter-mark-square-64.png'),
89 ]),
90 ],
91 ),
92 ])),
93 _ExampleCard(title: "Item h"),
94 _ExampleCard(title: "Item i"),
95 _ExampleCard(title: "Item j"),
96 _ExampleCard(title: "Item k"),
97 _ExampleCard(title: "Item l"),
98 ],
99 );
100 }
101}
102
103/// A filler card to show the video in a list of scrolling contents.
104class _ExampleCard extends StatelessWidget {
105 const _ExampleCard({Key key, this.title}) : super(key: key);
106
107 final String title;
108
109 @override
110 Widget build(BuildContext context) {
111 return Card(
112 child: Column(
113 mainAxisSize: MainAxisSize.min,
114 children: <Widget>[
115 ListTile(
116 leading: const Icon(Icons.airline_seat_flat_angled),
117 title: Text(title),
118 ),
119 ButtonBar(
120 children: <Widget>[
121 FlatButton(
122 child: const Text('BUY TICKETS'),
123 onPressed: () {
124 /* ... */
125 },
126 ),
127 FlatButton(
128 child: const Text('SELL TICKETS'),
129 onPressed: () {
130 /* ... */
131 },
132 ),
133 ],
134 ),
135 ],
136 ),
137 );
138 }
139}
140
141class _ButterFlyAssetVideo extends StatefulWidget {
142 @override
143 _ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState();
144}
145
146class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> {
147 VideoPlayerController _controller;
148
149 @override
150 void initState() {
151 super.initState();
152 _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4');
153
154 _controller.addListener(() {
155 setState(() {});
156 });
157 _controller.setLooping(true);
158 _controller.initialize().then((_) => setState(() {}));
159 _controller.play();
160 }
161
162 @override
163 void dispose() {
164 _controller.dispose();
165 super.dispose();
166 }
167
168 @override
169 Widget build(BuildContext context) {
170 return SingleChildScrollView(
171 child: Column(
172 children: <Widget>[
173 Container(
174 padding: const EdgeInsets.only(top: 20.0),
175 ),
176 const Text('With assets mp4'),
177 Container(
178 padding: const EdgeInsets.all(20),
179 child: AspectRatio(
180 aspectRatio: _controller.value.aspectRatio,
181 child: Stack(
182 alignment: Alignment.bottomCenter,
183 children: <Widget>[
184 VideoPlayer(_controller),
185 _ControlsOverlay(controller: _controller),
186 VideoProgressIndicator(_controller, allowScrubbing: true),
187 ],
188 ),
189 ),
190 ),
191 ],
192 ),
193 );
194 }
195}
196
197class _BumbleBeeRemoteVideo extends StatefulWidget {
198 @override
199 _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState();
200}
201
202class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
203 VideoPlayerController _controller;
204
205 Future<ClosedCaptionFile> _loadCaptions() async {
206 final String fileContents = await DefaultAssetBundle.of(context)
207 .loadString('assets/bumble_bee_captions.srt');
208 return SubRipCaptionFile(fileContents);
209 }
210
211 @override
212 void initState() {
213 super.initState();
214 _controller = VideoPlayerController.network(
215 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
216 closedCaptionFile: _loadCaptions(),
217 videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
218 );
219
220 _controller.addListener(() {
221 setState(() {});
222 });
223 _controller.setLooping(true);
224 _controller.initialize();
225 }
226
227 @override
228 void dispose() {
229 _controller.dispose();
230 super.dispose();
231 }
232
233 @override
234 Widget build(BuildContext context) {
235 return SingleChildScrollView(
236 child: Column(
237 children: <Widget>[
238 Container(padding: const EdgeInsets.only(top: 20.0)),
239 const Text('With remote mp4'),
240 Container(
241 padding: const EdgeInsets.all(20),
242 child: AspectRatio(
243 aspectRatio: _controller.value.aspectRatio,
244 child: Stack(
245 alignment: Alignment.bottomCenter,
246 children: <Widget>[
247 VideoPlayer(_controller),
248 ClosedCaption(text: _controller.value.caption.text),
249 _ControlsOverlay(controller: _controller),
250 VideoProgressIndicator(_controller, allowScrubbing: true),
251 ],
252 ),
253 ),
254 ),
255 ],
256 ),
257 );
258 }
259}
260
261class _ControlsOverlay extends StatelessWidget {
262 const _ControlsOverlay({Key key, this.controller}) : super(key: key);
263
264 static const _examplePlaybackRates = [
265 0.25,
266 0.5,
267 1.0,
268 1.5,
269 2.0,
270 3.0,
271 5.0,
272 10.0,
273 ];
274
275 final VideoPlayerController controller;
276
277 @override
278 Widget build(BuildContext context) {
279 return Stack(
280 children: <Widget>[
281 AnimatedSwitcher(
282 duration: Duration(milliseconds: 50),
283 reverseDuration: Duration(milliseconds: 200),
284 child: controller.value.isPlaying
285 ? SizedBox.shrink()
286 : Container(
287 color: Colors.black26,
288 child: Center(
289 child: Icon(
290 Icons.play_arrow,
291 color: Colors.white,
292 size: 100.0,
293 ),
294 ),
295 ),
296 ),
297 GestureDetector(
298 onTap: () {
299 controller.value.isPlaying ? controller.pause() : controller.play();
300 },
301 ),
302 Align(
303 alignment: Alignment.topRight,
304 child: PopupMenuButton<double>(
305 initialValue: controller.value.playbackSpeed,
306 tooltip: 'Playback speed',
307 onSelected: (speed) {
308 controller.setPlaybackSpeed(speed);
309 },
310 itemBuilder: (context) {
311 return [
312 for (final speed in _examplePlaybackRates)
313 PopupMenuItem(
314 value: speed,
315 child: Text('${speed}x'),
316 )
317 ];
318 },
319 child: Padding(
320 padding: const EdgeInsets.symmetric(
321 // Using less vertical padding as the text is also longer
322 // horizontally, so it feels like it would need more spacing
323 // horizontally (matching the aspect ratio of the video).
324 vertical: 12,
325 horizontal: 16,
326 ),
327 child: Text('${controller.value.playbackSpeed}x'),
328 ),
329 ),
330 ),
331 ],
332 );
333 }
334}
335
336class _PlayerVideoAndPopPage extends StatefulWidget {
337 @override
338 _PlayerVideoAndPopPageState createState() => _PlayerVideoAndPopPageState();
339}
340
341class _PlayerVideoAndPopPageState extends State<_PlayerVideoAndPopPage> {
342 VideoPlayerController _videoPlayerController;
343 bool startedPlaying = false;
344
345 @override
346 void initState() {
347 super.initState();
348
349 _videoPlayerController =
350 VideoPlayerController.asset('assets/Butterfly-209.mp4');
351 _videoPlayerController.addListener(() {
352 if (startedPlaying && !_videoPlayerController.value.isPlaying) {
353 Navigator.pop(context);
354 }
355 });
356 }
357
358 @override
359 void dispose() {
360 _videoPlayerController.dispose();
361 super.dispose();
362 }
363
364 Future<bool> started() async {
365 await _videoPlayerController.initialize();
366 await _videoPlayerController.play();
367 startedPlaying = true;
368 return true;
369 }
370
371 @override
372 Widget build(BuildContext context) {
373 return Material(
374 elevation: 0,
375 child: Center(
376 child: FutureBuilder<bool>(
377 future: started(),
378 builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
379 if (snapshot.data == true) {
380 return AspectRatio(
381 aspectRatio: _videoPlayerController.value.aspectRatio,
382 child: VideoPlayer(_videoPlayerController),
383 );
384 } else {
385 return const Text('waiting for video to load');
386 }
387 },
388 ),
389 ),
390 );
391 }
392}
Crypto
این پکیج (+) روی همه پلتفرمها کار میکند. این پکیج که از سوی تیم خود دارت توسعه یافته است، در واقع یک مجموعه از تابعهای هش رمزنگارانه است که در دارت خالص پیادهسازی شدهاند. این بدان معنی است که نیازی به کتابخانههای بیرونی برای عملیاتی ساختن آن ندارید. این پکیج از الگوریتمهای هشسازی زیر پشتیبانی میکند:
- SHA-1
- SHA-224
- SHA-256
- SHA-384
- SHA-512
- MD5
- HMAC (i.e. HMAC-MD5, HMAC-SHA1, HMAC-SHA256)
از آنجا که این پکیج یک ابزار GUI محسوب نمیشود و صرفاً یک کتابخانه رمزنگاری است، روی همه پلتفرمهای مختلف کار میکند.
carousel_slider
این پکیج (+) روی پلتفرمهای اندروید، iOS و وب کار میکند.
یک اسلایدر کاروسل بخشی از بسیاری از وباپلیکیشنها و وبسایتها محسوب میشود. پلاگین carousel_slider یک اسلایدر زیبا و قابل سفارشیسازی عرضه میکند که روی پلتفرمهای مختلف کار میکند.
از آنجا که این پلاگین ویجتهای مختلف را به عنوان محتوا میپذیرد، میتوانید هر چیزی را که یک ویجت باشد را اسلاید کنید. در قطعه کد زیر، مثالی از شیوه ساخت یک کاروسل را در اپلیکیشن فلاتر مشاهده میکنید:
1CarouselSlider(
2 options: CarouselOptions(height: 400.0),
3 items: [1,2,3,4,5].map((i) {
4 return Builder(
5 builder: (BuildContext context) {
6 return Container(
7 width: MediaQuery.of(context).size.width,
8 margin: EdgeInsets.symmetric(horizontal: 5.0),
9 decoration: BoxDecoration(
10 color: Colors.amber
11 ),
12 child: Text('text $i', style: TextStyle(fontSize: 16.0),)
13 );
14 },
15 );
16 }).toList(),
17)
کاروسل چند گزینه قابل پیکربندی به شرح زیر دارد:
- ارتفاع و نسبت ابعادی
- فعالسازی اسکرول بینهایت
- معکوس کردن جهت کاروسل
- فعالسازی پخش خودکار با بازه زمانی و مدت انیمیشن قابل پیکربندی
- تعریف کردن جهت اسکرول (افقی یا عمودی)
Path
این پکیج (+) روی پلتفرمهای اندروید، iOS و وب کار میکند. مسیرها هم از یک سو ساده و از سوی دیگر به طرز خارقالعادهای پیچیده هستند، زیرا بسته به هر پلتفرم متفاوت هستند. برای این که مطمئن شویم که هیچ نوع آسیبپذیری به شکل باگ یا مشکل امنیتی در کد خود نداریم، باید در زمان کار با مسیرها، همواره از یک کتابخانه مسیر استفاده کنیم. جهت الحاق یک دایرکتوری و یک فایل با استفاده از جداکننده فایل روی سیستم عامل به صورت زیر عمل میکنیم:
import 'package:path/path.dart' as p; p.join('directory', 'file.txt');
location
این پکیج (+) روی پلتفرمهای اندروید، iOS، وب و MacOS عمل میکند. یکی از مهمترین نکتهها در مورد گوشیهای تلفن، امکان تحرک آنها است که با توانایی ردگیری دقیق موقعیت ترکیب میشود. این امکانات موجب شده که کاربردهای بسیار مفیدی به دست آوریم. پلاگین location در فلاتر امکان دسترسی به موقعیت کنونی کاربر را تا حدود زیادی تسهیل میکند. این پلاگین زمانی که موقعیت تغییر یابد، Callback-هایی ارائه میکند. همچنین نقاط انتهایی API ارائه میکند که امکان دسترسی درخواست مناسب را به موقعیت کاربر را فراهم میسازد.
سخن پایانی
به این ترتیب به پایان این مقاله با موضوع معرفی 10 پکیج برتر و محبوب فلاتر میرسیم. امیدواریم مواردی که در این مقاله مطرح شدند، مورد توجه شما قرار گرفته باشند، به طوری که بتوانید در پروژههای فلاتر خود از آنها بهره بگیرید.