پیاده سازی تکمیل خودکار فهرست جستجو در فلاتر — از صفر تا صد
قابلیت جستجو امروزه تقریباً در همه اپلیکیشنهای وب و موبایل دیده میشود و به طور روزمره مورد استفاده قرار میگیرد. هر اپلیکیشن موبایل مانند توییتر را در نظر بگیرید، اگر بخواهید به دنبال یک فرد یا هشتگ بگردید، به محض این که شروع به نوشتن بکنید، میبینید که پیشنهادهایی در یک لیست بازشدنی ظاهر میشوند. جستجوی گوگل بهترین مثال برای این قابلیت است. به محض شروع به نوشتن یک کلیدواژه، پیشنهادهایی ارائه میکند که میتوان از میان آنها انتخاب کرد. زمانی که گزینه مطلوب انتخاب شد، متن انتخابی در فیلد جستجو نمایش پیدا میکند. این فرایند موجب سهولت زیادی در کار میشود. در این مقاله شیوه پیادهسازی فهرست جستجوی تکمیل خودکار را در اپلیکیشنهای فلاتر معرفی میکنیم.
نکته: توجه کنید که بین دو واژه «تکمیل خودکار» و «پیشنهاد خودکار» تفاوت وجود دارد و ما در این مقاله از فهرست تکمیل خودکار صحبت میکنیم. برای درک تفاوت کلیدی بین این دو اصطلاح به این صفحه (+) مراجعه کنید.
کاربرد
ما به دلیل مقاصد آموزشی یک ورودی Textfield ساده میسازیم که به محض وارد کردن نام بازیکن، نام کامل وی به همراه کشوری که در آن بازی میکند در یک فهرست ظاهر میشود تا امکان تکمیل خودکار جستجو وجود داشته باشد. زمانی که کاربری مدخلی را از فهرست انتخاب کند، آن مدخل در Textfield نمایش مییابد.
منابع داده
ما دادهها را از یک فایل JSON محلی استخراج میکنیم که در آن از قبل keyword و کلمه autocomplete مربوطه کنار هم تعریف شدهاند. مقادیر country نیز که در نهایت در فهرست و زمانی که کاربر کلیدواژه را در رابط کاربری وارد میکند، نمایش پیدا میکنند، در همین فایل تعریف شدهاند.
قطعه کد زیر ساختار فایل JSON را نشان میدهد:
فلاتر افزونههای مختلفی برای پیادهسازی تکمیل خودکار در یک ورودی متنی ارائه میکند. افزونهای که ما در این راهنما مورد استفاده قرار میدهیم Autocomplete_TextField است. دلایل استفاده از این افزونه به شرح زیر است:
- از چند نوع داده پشتیبانی میکند و استفاده آسانی دارد.
- این پکیج از سوی مالک آن به طور مرتب بهروزرسانی میشود و بهبودهای خوبی مانند پشتیبانی از شیء علاوه بر مقادیر رشتهای نیز دارد که امکان تبدیل یک آیتم به کلاس را به جای String به دست میدهد.
کار خود را با ایجاد یک پروژه جدید فلاتر آغاز میکنیم:
flutter create autocomplete_demo cd autocomplete_demo flutter run
کد موجود در main.dart را حذف میکنیم و ابتدا پکیج را در pubspec.yaml اضافه میکنیم:
1autocomplete_textfield: ^1.6.4
دستور packages get را اجرا کنید تا پکیج مورد ارجاع را نصب کنید و همچنین پکیج مورد نظر را در main.dart ایمپورت نمایید:
1import ‘package:autocomplete_textfield/autocomplete_textfield.dart’;
همچنان که قبلاً اشاره کردیم، از یک فایل JSON محلی به عنوان منبع داده خود استفاده میکنیم و باید فایل را در پوشه assets به صورت زیر اضافه کنیم:
1assets:
2 - assets/players.json
از آنجا که این پروژه جدیدی است که ساختهایم، باید پوشه assets را در محل ریشه ایجاد کرده و JSON را مانند زیر کپی کرده و packages get را اجرا کنیم:
برای واکشی دادهها JSON باید ابتدا آن را تجزیه کنیم. به این منظور یک شیء Plain Old Dart میسازیم تا نماینده ساختار JSON باشد. نام آن را players.dart میگذاریم:
1import 'dart:convert';
2
3import 'package:flutter/services.dart';
4
5class Players {
6 String keyword;
7 int id;
8 String autocompleteterm;
9 String country;
10
11 Players({
12 this.keyword,
13 this.id,
14 this.autocompleteterm,
15 this.country
16 });
17
18 factory Players.fromJson(Map<String, dynamic> parsedJson) {
19 return Players(
20 keyword: parsedJson['keyword'] as String,
21 id: parsedJson['id'],
22 autocompleteterm: parsedJson['autocompleteTerm'] as String,
23 country: parsedJson['country'] as String
24 );
25 }
26}
توجه کنید که بررسی جزییات تجزیه ساختمان JSON خارج از حیطه این مقاله است.
اکنون ما شیئی از ساختمان JSON داریم که به آن ارجاع بدهیم، اما باید بتوانیم آن را دیکود کرده و دادهها را با ارائه مسیر JSON محلی نیز بارگذاری کنیم. به این منظور کلاس دیگری به نام PlayersViewModel در players.dart ایجاد میکنیم که در آن لیست استاتیکی از کلاس Players ایجاد کرده و دادههای دیکود شده JSON را اضافه میکنیم.
1class PlayersViewModel {
2 static List<Players> players;
3
4 static Future loadPlayers() async {
5 try {
6 players = new List<Players>();
7 String jsonString = await rootBundle.loadString('assets/players.json');
8 Map parsedJson = json.decode(jsonString);
9 var categoryJson = parsedJson['players'] as List;
10 for (int i = 0; i < categoryJson.length; i++) {
11 players.add(new Players.fromJson(categoryJson[i]));
12 }
13 } catch (e) {
14 print(e);
15 }
16 }
متد ()loadPlayers را از فایل main.dart درون ()initState فراخوانی میکنیم تا دادهها در اختیار ما قرار گیرند و در زمان اجرای اپلیکیشن بتوانیم از آنها به سهولت استفاده کنیم.
در فایل main.dart ساختمان اصلی دمو را میسازیم. کد مرتبط با ورودی متنی تکمیل خودکار را گام به گام اضافه خواهیم کرد:
1import 'package:flutter/material.dart';
2
3void main() => runApp(MyApp());
4
5class MyApp extends StatelessWidget {
6 @override
7 Widget build(BuildContext context) {
8 return MaterialApp(
9 home: Scaffold(
10 body: AutoComplete(),
11 ),
12 );
13 }
14}
15
16class AutoComplete extends StatefulWidget {
17
18 @override
19 _AutoCompleteState createState() => new _AutoCompleteState();
20}
21
22class _AutoCompleteState extends State<AutoComplete> {
23
24 _AutoCompleteState();
25
26 @override
27 Widget build(BuildContext context) {
28 return Scaffold(
29 appBar: AppBar(
30 title: Text('Auto Complete List Demo'),
31 ),
32 body: new Center(
33 child: new Column(
34 children: <Widget>[
35 new Column(children: <Widget>[ //AutoCompleteTextField code here
36 ]),
37 ])));
38 }
39}
کد فوق کاملاً سرراست است و نکته تازهای ندارد. اینک UI به صورت زیر در آمده است:
اکنون textfield را اضافه خواهیم کرد. به جای استفاده از ویجت TextField در فلاتر از AutoCompleteTextField استفاده میکنیم. این کلاس مشخصهها و متدهای ساده و متنوعی دارد. این موارد در ادامه مقاله بیشتر بررسی میکنیم، اما اینک از این کلاس مانند زیر درون کلاس AutoCompleteState_ استفاده میکنیم:
1AutoCompleteTextField searchTextField;
از آنجا که AutoCompleteTextField یک اکستنشن از ویجت TextField است، همه مشخصههای خود را که برای ساخت و نمایش textfield ابتدایی شامل متن سرنخ ضروری هستند به ارث میبرد. ابتدا این مورد را اضافه میکنیم. درون تابع build و زیر ویجت Column کد زیر را اضافه میکنیم تا textfield مقدماتی با یک سرنخ نمایش پیدا کند:
1searchTextField = AutoCompleteTextField<Players>(
2 style: new TextStyle(color: Colors.black, fontSize: 16.0),
3decoration: new InputDecoration(
4 suffixIcon: Container(
5 width: 85.0,
6 height: 60.0,
7 ),
8 contentPadding: EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
9 filled: true,
10 hintText: 'Search Player Name',
11 hintStyle: TextStyle(color: Colors.black)),
چنان که احتمالاً متوجه شدهاید، کلاس Players به عنوان نوع داده به سازنده AutoCompleteTextField ارسال شده است، چون امکان ارائه هر نوع داده را به صورت پیشنهاد فراهم میسازد. کد فوق در UI به صورت زیر رندر میشود:
سپس اگر ماوس را روی خط کد AutoCompleteTextField<Players> از شما میخواهد که پارامترهای الزامی را پیادهسازی کنید. این موارد به صورت زیر هستند:
ابتدا آنها را یک به یک بررسی میکنیم و کمکی که هر کدام به ما در پیادهسازی لیست تکمیل خودکار دارند را توضیح میدهیم.
itemBuilder
این یک callback برای ساخت هر آیتم در لیستی است که قرار است نمایش یابد و شامل دادههای پیشنهادی است و یک ویجت بازگشت میدهد. ما کدی را درون این پارامتر اضافه میکنیم که در عمل دادههای AutoCompleteTerm و country را در زمانی که کاربر کلیدواژهای را وارد میکند رندر خواهد کرد.
1itemBuilder: (context, item) {
2 return Row(
3 mainAxisAlignment: MainAxisAlignment.spaceBetween,
4 children: <Widget>[
5 Text(item.autocompleteterm,
6 style: TextStyle(
7 fontSize: 16.0
8 ),),
9 Padding(
10 padding: EdgeInsets.all(15.0),
11 ),
12 Text(item.country,
13 )
14 ],
15 );
16},
itemFilter
این یک callback برای فیلتر کردن آیتم است و بسته به متن ورودی مقدار درست/نادرست بازگشت میدهد. اگر یک تطبیق با نام بازیکن وارد شده پیدا شود، مقدار درست بازگشت مییابد و متن مرتبط نمایش پیدا میکند و در غیر این صورت هیچ نام تطبیق یافتهای نشان داده نمیشود.
1itemFilter: (item, query) {
2 return item.autocompleteterm
3 .toLowerCase()
4 .startsWith(query.toLowerCase());
5}
itemSorter
یک callback برای مرتبسازی آیتمها محسوب میشود.
1itemSorter: (a, b) {
2 return a.autocompleteterm.compareTo(b.autocompleteterm);
3},
itemSubmitted
این یک callback روی آیتم منتخب است. پس از این که آیتم از لیست انتخاب شد، این پارامتر آن آیتم را در textfield تنظیم میکند. از آنجا که باید آیتم منتخب را در Textfield حفظ کنیم، کد مورد نظر را درون متد ()setState قرار میدهیم. کد درون آن، آیتم منتخب را گرفته و آن را در Textfield قرار میدهد.
برای تعیین/بازیابی مقدار از Textfield باید از کلاس TextEditingController استفاده کنیم، به طوری که هر زمان هر مقداری در TextEditingController بهروزرسانی شد یا متن تغییر یافت، بتوانیم به کنترلر گوش دهیم. به این منظور باید controller را به عنوان یک وهله جدید از کلاس TextEditingController به صورت زیر اعلان کنیم:
1TextEditingController controller = new TextEditingController();
2itemSubmitted: (item) {
3 setState(() => searchTextField.textField.controller.text = item.autocompleteterm);
4},
Key
یک کلید عمومی از نوع GlobalKey<AutoCompleteTextFieldState<T>> است که برای فعالسازی افزودن پیشنهادها به textfiled ضروری است. همچنین متد ()clear میتواند در صورت نیاز برای پاک کردن فراخوانی شود.
1GlobalKey<AutoCompleteTextFieldState<Players>> key = new GlobalKey();
2key: key,
Suggestions
این گزینه پیشنهادهایی را که قرار است در رابط کاربری نمایش پیدا کنند فعال میکند و ما وهلهای از کلاس PlayersViewModel را در این جا فرا میخوانیم تا پیشنهادها را بارگذاری کند.
1suggestions: PlayersViewModel.players,
بدین ترتیب ما همه پارامترهای الزامی را از کلاس AutoCompleteTextField پیادهسازی کردهایم. اما چنان که قبلاً اشاره کردیم باید دادههای بازیکن را با فراخوانی متد loadPlayers که در players.dart بارگذاری کنیم. این کار را با ایجاد یک متد viod به نام ()loadData_ و فراخوانی ()PlayersViewModel.loadPlayers انجام میدهیم:
1void _loadData() async {
2 await PlayersViewModel.loadPlayers();
3}
4
5@override
6void initState() {
7 _loadData();
8 super.initState();
9}
اکنون اپلیکیشن را اجرا میکنیم و خروجی را تماشا میکنیم:
اینک زمانی که کلیدواژهای را به صورت تک حرف یا کلمه وارد کنیم، پیشنهادهای منطبق شروع به نمایش پیدا میکنند.
اما هنوز یک بخش مهم ناقص است. اگر نامی را از لیست انتخاب کنیم، آن نام باید در textfield نمایش پیدا کند اما فعلاً این اتفاق نمیافتد. AutoCompleteTextField پارامتری به نام clearOnSubmit دارد که یک مقدار بولی میگیرد تا در زمان ارسال autocompletetextfield را پاک کند. ما باید آن را روی flase تنظیم کنیم تا نام منتخب در فیلد نمایش یابد.
اینک خروجی کامل را روی هر دو پلتفرم نگاه میکنیم:
بدین ترتیب با موفقیت قابلیت تکمیل خودکار لیست جستجو را پیادهسازی کردیم. برای مشاهده کد کامل این اپلیکیشن به این ریپوی گیتهاب (+) مراجعه کنید.
همچنین میتوانید ظاهر UI و حس و حال لیست تکمیل خودکار و متن درون آن را شخصیسازی کنید. به این منظور ویجت Row را درون یک Container قرار دهید که امکان تغییر رنگ پسزمینه لیست و استایلبندی متن را بسته به نیاز فراهم میکند. به مثال زیر توجه کنید:
1itemBuilder: (context, item) {
2 return Container(
3 color: Colors.blueAccent,
4 child: Row(
5 mainAxisAlignment: MainAxisAlignment.spaceBetween,
6 children: <Widget>[
7 Text(item.autocompleteterm,
8 style: TextStyle(
9 fontSize: 16.0
10 ),),
11 Padding(
12 padding: EdgeInsets.all(15.0),
13 ),
14 Text(item.country,
15 style: TextStyle(
16 fontWeight: FontWeight.bold
17 ),
18 )
19 ],
20 )
21 );
22},
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی اندروید
- مجموعه آموزشهای برنامهنویسی
- مجموعه آموزشهای دروس علوم و مهندسی کامپیوتر
- گوگل فلاتر (Flutter) از صفر تا صد — ساخت اپلیکیشن به کمک ویجت
- آموزش گوگل فلاتر (Flutter ): ساخت اپلیکیشن دستورهای آشپزی
- ساخت بازی پازل با فلاتر — از صفر تا صد
==