Tổng quan
Flutter cung cấp widget Navigator để quản lý và thao tác với stack khi thực hiện điều hướng các màn hình.
Trong quá trình phát triển app mobile chúng ta sẽ có một số case điều hướng cơ bản cần phải xử lý như hình bên trên, hãy xem flutter hỗ trợ giải quyết các case điều hướng đó như thế nào nhé
Note nhỏ
Navigator cung cấp 2 loại function là
1 2 3 4 |
Navigator<span class="token punctuation">.</span><span class="token function">pushNamed</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> string<span class="token punctuation">)</span> Navigator<span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">pushNamed</span><span class="token punctuation">(</span>string<span class="token punctuation">)</span> |
hai cách gọi bên trên là tương đương và nếu bạn đọc source thì Navigator.pushNamed(context, string)
là hàm static gọi đến Navigator.pushNamed(context, string)
1. push, pop
Hai hàm cơ bản nhất và hay sử dụng nhất khi thực hiện các thao tác navigation
push
Thực hiện push widget vào stack của navigator, mỗi lần gọi hàm là một lần push widget vào stack
Gồm có 2 loại là:
push(context, route)
pushNamed(context, string)
push(context, route)
1 2 3 4 5 6 7 8 9 10 11 |
Navigator<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span> context<span class="token punctuation">,</span> <span class="token function">MaterialPageRoute</span><span class="token punctuation">(</span>builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">Screen1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// or</span> Navigator<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span> context<span class="token punctuation">,</span> <span class="token function">MaterialPageRoute</span><span class="token punctuation">(</span>builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// do something</span> <span class="token keyword">return</span> <span class="token function">Screen1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Cách này cho bạn kiểm soát tốt hơn việc khở tạo màn hình mới, giúp bạn có thể thực hiện thêm thao tác tiền xử lý, hoặc truyền param cho màn mới, …
pushNamed(context, string)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span class="token keyword">class</span> <span class="token class-name">Routes</span> <span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token keyword">final</span> String screen1 <span class="token operator">=</span> <span class="token string">"/screen1"</span><span class="token punctuation">;</span> <span class="token keyword">static</span> <span class="token keyword">final</span> String screen2 <span class="token operator">=</span> <span class="token string">"/screen2"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">MaterialApp</span><span class="token punctuation">(</span> routes<span class="token punctuation">:</span> <span class="token punctuation">{</span> Routes<span class="token punctuation">.</span>screen1<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">Screen1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> Routes<span class="token punctuation">.</span>screen2<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">Screen2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> Navigator<span class="token punctuation">.</span><span class="token function">pushNamed</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> Routes<span class="token punctuation">.</span>screen1<span class="token punctuation">)</span><span class="token punctuation">;</span> |
Bên trên là định nghĩa hết các name trong 1 class Routes, ngoài ra bạn có thể định nghĩa name trong cục bộ widget
1 2 3 4 |
<span class="token keyword">class</span> <span class="token class-name">Screen1</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token keyword">final</span> String screen1 <span class="token operator">=</span> <span class="token string">"/screen1"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Cách này giúp bạn định nghĩa route ngắn gọn, nhưng bị giới hạn khi routeNamed sẽ trả về constructor cố định
pop(context)
Thực hiện pop widget ở trên cùng của stack navigator, mỗi lần gọi là một lần pop cho đến khi stack hết widget.
1 2 |
Navigator<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> |
2. Truyền data từ A push B
Từ màn A, mở màn B và bạn muốn truyền thêm một vài thông tin thì có 2 cách để thực hiện:
- Truyền qua constructor của B
- Truyền qua argumments
Truyền qua constructor
Để thực hiện cách này thì ở class A bạn sẽ cần phải dùng push(context, route)
.
Ở bên class B thì chỉ cần gọi var là có giá tị
1 2 3 4 5 6 7 8 9 10 11 12 13 |
classs B <span class="token punctuation">{</span> <span class="token keyword">final</span> String title<span class="token punctuation">;</span> <span class="token function">B</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token metadata symbol">@require</span> <span class="token keyword">this</span><span class="token punctuation">.</span>title<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{</span> <span class="token function">toB</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Navigator<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span> context<span class="token punctuation">,</span> <span class="token function">MaterialPageRoute</span><span class="token punctuation">(</span>builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">B</span><span class="token punctuation">(</span><span class="token string">'from A to B'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Truyền qua arguments
Các hàm push có hỗ trợ optional param arguments đều hỗ trợ việc truyền data.
Các bạn có thể dùng push(context, route, arguments)
hoặc pushNamed(context, string, arguments)
để thực hiên truyền từ A.
Tại B để nhận thì cần lấy ra từ arrguments.
1 2 3 4 5 6 7 8 |
<span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{</span> <span class="token function">pushNamed</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> <span class="token string">"/B"</span><span class="token punctuation">,</span> arguments<span class="token punctuation">:</span> <span class="token string">"from A to B"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token punctuation">{</span> String args <span class="token operator">=</span> ModalRoute<span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span>settings<span class="token punctuation">.</span>arguments <span class="token punctuation">}</span> |
Lưu ý: do arguments là một kiểu object nên khi muốn truyền nhiều loại data khác nhau thì cần phải tạo object wrap hết những type bạn cần truyền.
3. return data từ B về A
Để truyền dữ liệu từ B về A thì dùng pop(context, result)
với param result là dữ liệu bạn muốn trả về.
Tại A, hàm push trả về future nên việc await hàm push sẽ nhận được dữ liệu từ B
1 2 3 4 5 6 7 8 |
<span class="token keyword">class</span> <span class="token class-name">B</span> <span class="token punctuation">{</span> Navigator<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> result<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">A</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> result <span class="token operator">=</span> <span class="token keyword">await</span> Navigator<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>B<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
4. Các hàm push khác
Navigator còn có một số hàm push khác để cho những case cần custom flow navigation như sau:
pushAndRemoveUntuil / pushNamedAndRemoveUntil
pushReplacement / pushReplacementNamed
popAndPushNamed
ở bên trên mình đã giải thích về push/ pushNamed
nên dưới đây mình chỉ nói về ý nghĩa của các hàm này chứ không nói đến cách thức khác nhau nữa.
pushAndRemoveUntil / pushNamedAndRemoveUntil (context, route/string, bool)
Thực hiện thêm widget vào stack và pop các widget trong stack cũ cho đến khi bool == true
Về mặt UI sẽ nhìn thấy enter animation của push widget mới vào.
1 2 3 4 5 6 |
Navigator<span class="token punctuation">.</span><span class="token function">pushAndRemoveUntil</span><span class="token punctuation">(</span> context<span class="token punctuation">,</span> <span class="token function">MaterialPageRoute</span><span class="token punctuation">(</span>builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">Screen1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> ModalRoute<span class="token punctuation">.</span><span class="token function">withName</span><span class="token punctuation">(</span><span class="token string">'/first'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Nếu bạn muốn pop hết các widget sẵn có trong stack thì có thể return false ở param bool
Use case:
- Sau khi thực hiện các bước purchase, push màn status và pop hết các màn purchase
- Sau khi thực hiện các thao tác và nhấn logout, pop hết các màn và push login
pushReplacement / pushReplacementNamed
Thực hiện push widget vào stack và pop widget hiện tại của stack
Về mặt UI sẽ nhìn thấy enter animation của push widget mới vào.
Use case:
- Từ màn splash mở màn Home
- Từ màn Login, login thành công mở màn Home
popAndPushNamed
Thực hiện pop widget hiện tại của stack và push widget mới vào. Về ý nghĩa thì giống pushReplacement
Tuy nhiên về mặt UI sẽ nhìn thấy exit animation của widget hiện tại bị pop
Use case:
- Khi thực hiện xem item list, mở filter, chọn và apply filter thì pop màn filter và push màn item list
5. Các hàm pop khác
Navigator còn có một số hàm pop khác để cho những case cần custom flow navigation như sau:
- popUntil
- canPop
- maybePop
Chúng ta cùng đi vào từng loại nhé
popUntil(bool)
Hàm này dễ hiểu rồi, pop widget trong stack cho đến khi bool == true
canPop
return false nếu đây là widget đầu tiên trong navigator stack, hay stack size = 1. Nếu stack size > 1 thì return true.
maybePop = if(canPop) pop
Nếu stack size lớn hơn 1 thì mới thực hiện pop còn không thì thôi
6. Các hàm khác
Các hàm sau của Navigator đều cần param route ( route = MaterialPagedRoute(builder: )). Nên để thực hiện thì bạn cần có refer đến route tương ứng mà muốn gọi hàm.
Hiện tại chưa thể get stack của navigator nên việc này sẽ hơi rắc rối một chút.
replaceRoute (context, oldRoute, newRoute)
replaceRouteBelow (context, anchorRoute, newRoute)
removeRoute (context, route)
removeRouteBelow (context, anchorRoute)
replaceRoute (context, oldRoute, newRoute)
replace oldROute trong stack bằng newRoute
replaceRouteBelow (context, anchorRoute, newRoute)
replace route ngay dưới anchorRoute trong stack bằng newRoute
removeRoute (context, route)
remove route trong stack
removeRouteBelow (context, anchorRoute)
remove route ngay dưới anchorRoute trong stack
Kết
Bài này mình đã giới thiệu tới các bạn về widget Navigator trong Flutter để xử lý các tác vụ navigation. Tùy theo yêu cầu cụ thể khi phát triển mà bạn sẽ chọn cho mình phương án phù hợp nhất.