feat: Finalize onboarding screen
BIN
StudioProjects/yimaru_app/assets/fonts/Aeonik-Regular.ttf
Normal file
5
StudioProjects/yimaru_app/assets/icons/alert.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="114" height="114" viewBox="0 0 114 114" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M66.1428 99.75H47.8572C25.8626 99.75 14.8654 99.75 10.8127 92.596C6.76008 85.4425 12.385 75.9591 23.6348 56.9929L32.7777 41.5783C43.5841 23.3595 48.9872 14.25 57 14.25C65.0128 14.25 70.4159 23.3594 81.2222 41.5783L90.3654 56.9929C101.615 75.9591 107.24 85.4425 103.187 92.596C99.1344 99.75 88.1372 99.75 66.1428 99.75Z" stroke="#9E2891" stroke-width="7.125" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M57 42.75V64.125" stroke="#9E2891" stroke-width="7.125" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M57 80.713V80.7604" stroke="#9E2891" stroke-width="7.125" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 753 B |
10
StudioProjects/yimaru_app/assets/icons/b1.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="125px" height="138px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g><path style="opacity:0.808" fill="#9e2891" d="M 55.5,-0.5 C 59.8333,-0.5 64.1667,-0.5 68.5,-0.5C 84.7704,8.97332 101.104,18.4733 117.5,28C 120.738,30.9978 123.071,34.4978 124.5,38.5C 124.5,58.5 124.5,78.5 124.5,98.5C 122.507,103.008 119.507,106.842 115.5,110C 99.2743,118.941 83.2743,128.108 67.5,137.5C 64.1667,137.5 60.8333,137.5 57.5,137.5C 41.2609,128.375 24.9276,119.208 8.5,110C 4.49334,106.842 1.49334,103.008 -0.5,98.5C -0.5,78.5 -0.5,58.5 -0.5,38.5C 1.15508,34.654 3.48842,31.154 6.5,28C 22.8963,18.4733 39.2296,8.97332 55.5,-0.5 Z"/></g>
|
||||||
|
<g><path style="opacity:1" fill="#e9cee5" d="M 42.5,55.5 C 42.5,64.5 42.5,73.5 42.5,82.5C 47.1667,82.5 51.8333,82.5 56.5,82.5C 51.6946,83.4872 46.6946,83.8205 41.5,83.5C 41.1731,73.985 41.5064,64.6517 42.5,55.5 Z"/></g>
|
||||||
|
<g><path style="opacity:1" fill="#fdfafc" d="M 42.5,55.5 C 47.1785,55.3342 51.8452,55.5008 56.5,56C 61.2911,57.4119 63.1244,60.5786 62,65.5C 61.1667,67 60,68.1667 58.5,69C 63.8662,71.8444 64.8662,75.8444 61.5,81C 59.9751,82.0086 58.3084,82.5086 56.5,82.5C 51.8333,82.5 47.1667,82.5 42.5,82.5C 42.5,73.5 42.5,64.5 42.5,55.5 Z"/></g>
|
||||||
|
<g><path style="opacity:1" fill="#fbf7fb" d="M 71.5,55.5 C 73.5,55.5 75.5,55.5 77.5,55.5C 77.5,64.8333 77.5,74.1667 77.5,83.5C 75.5,83.5 73.5,83.5 71.5,83.5C 71.5,77.5 71.5,71.5 71.5,65.5C 69.5,65.5 67.5,65.5 65.5,65.5C 65.5,64.1667 65.5,62.8333 65.5,61.5C 70.5,62.5 72.5,60.5 71.5,55.5 Z"/></g>
|
||||||
|
<g><path style="opacity:1" fill="#a63d9a" d="M 47.5,60.5 C 50.1873,60.3359 52.854,60.5026 55.5,61C 57.1397,63.7758 56.473,65.7758 53.5,67C 51.5273,67.4955 49.5273,67.6621 47.5,67.5C 47.5,65.1667 47.5,62.8333 47.5,60.5 Z"/></g>
|
||||||
|
<g><path style="opacity:1" fill="#a33596" d="M 47.5,71.5 C 50.1873,71.3359 52.854,71.5026 55.5,72C 58.033,74.084 58.033,76.084 55.5,78C 52.854,78.4974 50.1873,78.6641 47.5,78.5C 47.5,76.1667 47.5,73.8333 47.5,71.5 Z"/></g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
5
StudioProjects/yimaru_app/assets/icons/complete.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M63.9919 0C60.8419 0 57.6937 1.20941 55.3022 3.60086L50.6045 8.29848C46.3616 12.5413 40.6045 14.9155 34.6041 14.9155H27.1926C20.4286 14.9155 14.902 20.4419 14.902 27.206V32.6496V34.6214C14.902 40.6217 12.5277 46.3747 8.28487 50.6175L3.58719 55.3152C-1.19573 60.0981 -1.19573 67.9196 3.58719 72.7025L8.28487 77.4001C12.5277 81.643 14.902 87.3919 14.902 93.3922V100.808C14.902 107.572 20.4286 113.098 27.1926 113.098H34.6041C40.6045 113.098 46.3616 115.476 50.6045 119.719L55.3022 124.413C60.0851 129.196 67.8988 129.196 72.6817 124.413L77.3834 119.719C81.6263 115.476 87.3795 113.098 93.3798 113.098H100.791C107.555 113.098 113.082 107.572 113.082 100.808V93.3922C113.082 87.3919 115.476 81.643 119.719 77.4001L124.413 72.7025C129.196 67.9196 129.196 60.0981 124.413 55.3152L119.719 50.6175C115.476 46.3746 113.082 40.6217 113.082 34.6214V27.206C113.082 20.4419 107.555 14.9155 100.791 14.9155H93.3798C87.3795 14.9155 81.6263 12.5414 77.3834 8.29848L72.6817 3.60086C70.2902 1.20941 67.142 7.80299e-06 63.9919 0Z" fill="#9E2891" fill-opacity="0.2"/>
|
||||||
|
<path d="M63.9929 8.00006C61.2366 8.00006 58.482 9.0583 56.3894 11.1508L52.2789 15.2612C48.5664 18.9737 43.5289 21.0511 38.2786 21.0511H31.7936C25.875 21.0511 21.0393 25.8867 21.0393 31.8053V36.5684V38.2938C21.0393 43.5441 18.9618 48.5779 15.2493 52.2904L11.1388 56.4009C6.95374 60.5859 6.95374 67.4297 11.1388 71.6148L15.2493 75.7252C18.9618 79.4377 21.0393 84.468 21.0393 89.7183V96.2068C21.0393 102.125 25.875 106.961 31.7936 106.961H38.2786C43.5289 106.961 48.5664 109.042 52.2789 112.754L56.3894 116.861C60.5745 121.046 67.4115 121.046 71.5965 116.861L75.7105 112.754C79.423 109.042 84.457 106.961 89.7073 106.961H96.1924C102.111 106.961 106.947 102.125 106.947 96.2068V89.7183C106.947 84.468 109.042 79.4377 112.754 75.7252L116.861 71.6148C121.046 67.4297 121.046 60.5859 116.861 56.4009L112.754 52.2904C109.042 48.5779 106.947 43.5441 106.947 38.2938V31.8053C106.947 25.8867 102.111 21.0511 96.1924 21.0511H89.7073C84.457 21.0511 79.423 18.9738 75.7105 15.2612L71.5965 11.1508C69.504 9.05829 66.7492 8.00007 63.9929 8.00006Z" fill="#9E2891"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M81.9362 50.3204C82.8406 51.1118 82.9227 52.4904 82.1182 53.3832L59.4729 78.5182C58.6643 79.4157 57.2758 79.4727 56.3963 78.6448L43.2922 66.3084C42.475 65.5391 42.3968 64.2571 43.0979 63.3807C43.8716 62.4136 45.3123 62.2805 46.233 63.1087L56.4 72.2537C57.2836 73.0485 58.6439 72.9774 59.4399 72.095L78.9182 50.4986C79.7063 49.6246 81.0506 49.5454 81.9362 50.3204Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
15
StudioProjects/yimaru_app/assets/icons/logo.svg
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="137px" height="45px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g><path style="opacity:0.855" fill="#fefffe" d="M 17.5,-0.5 C 18.8333,-0.5 20.1667,-0.5 21.5,-0.5C 24.7669,1.81111 28.1002,4.14444 31.5,6.5C 27.8282,9.33799 23.8282,11.6713 19.5,13.5C 15.1718,11.6713 11.1718,9.33799 7.5,6.5C 10.8514,4.14144 14.1847,1.80811 17.5,-0.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.694" fill="#fefffe" d="M 62.5,5.5 C 63.6946,5.86562 64.3613,6.69895 64.5,8C 64.3178,12.3314 64.3178,16.8314 64.5,21.5C 63.1667,21.5 61.8333,21.5 60.5,21.5C 60.2065,17.5633 60.5399,13.73 61.5,10C 61,9.5 60.5,9 60,8.5C 56.7899,12.2511 55.2899,16.5844 55.5,21.5C 54.1667,21.5 52.8333,21.5 51.5,21.5C 51.5405,16.243 49.8738,11.5763 46.5,7.5C 47.723,6.38615 49.0563,6.21948 50.5,7C 51.7875,8.40952 52.9542,9.90952 54,11.5C 55.3906,7.36888 58.224,5.36888 62.5,5.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.93" fill="#fefffe" d="M 25.5,44.5 C 21.5,44.5 17.5,44.5 13.5,44.5C 13.8282,39.1254 13.4948,33.7921 12.5,28.5C 8.27197,25.8873 3.93863,23.554 -0.5,21.5C -0.5,20.5 -0.5,19.5 -0.5,18.5C 1.72466,15.7376 3.72466,12.7376 5.5,9.5C 10.1667,12.1667 14.8333,14.8333 19.5,17.5C 24.0825,14.9593 28.5825,12.2926 33,9.5C 35.549,12.765 37.7157,16.265 39.5,20C 35.538,23.32 31.2047,26.1534 26.5,28.5C 25.5052,33.7921 25.1718,39.1254 25.5,44.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.752" fill="#fefffe" d="M 65.5,10.5 C 70.9897,10.8823 76.323,11.0489 81.5,11C 82.4558,14.3936 82.7892,17.8936 82.5,21.5C 81.5,21.5 80.5,21.5 79.5,21.5C 79.6633,19.1432 79.4966,16.8098 79,14.5C 78.586,14.0426 78.086,13.7093 77.5,13.5C 76.52,16.0865 76.1866,18.7531 76.5,21.5C 75.1667,21.5 73.8333,21.5 72.5,21.5C 72.5,18.8333 72.5,16.1667 72.5,13.5C 71.5,13.5 70.5,13.5 69.5,13.5C 69.5,16.1667 69.5,18.8333 69.5,21.5C 68.1667,21.5 66.8333,21.5 65.5,21.5C 65.5,17.8333 65.5,14.1667 65.5,10.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.731" fill="#fefffe" d="M 85.5,10.5 C 88.1194,10.2317 90.6194,10.565 93,11.5C 93.4552,14.8536 93.9552,18.1869 94.5,21.5C 91.0678,21.4096 87.5678,21.0763 84,20.5C 83.1751,18.2992 83.6751,16.4659 85.5,15C 84.6919,14.6924 84.0253,14.1924 83.5,13.5C 84.1925,12.4822 84.8592,11.4822 85.5,10.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.753" fill="#fefffe" d="M 95.5,10.5 C 99.1667,10.5 102.833,10.5 106.5,10.5C 106.5,13.1667 106.5,15.8333 106.5,18.5C 107.5,18.5 108.5,18.5 109.5,18.5C 109.5,15.8333 109.5,13.1667 109.5,10.5C 110.833,10.5 112.167,10.5 113.5,10.5C 113.5,14.1667 113.5,17.8333 113.5,21.5C 110.644,21.0717 107.644,20.905 104.5,21C 103.029,18.7572 102.029,16.2572 101.5,13.5C 98.828,15.4255 97.828,18.0921 98.5,21.5C 97.5,21.5 96.5,21.5 95.5,21.5C 95.5,17.8333 95.5,14.1667 95.5,10.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.736" fill="#fefffe" d="M 51.5,22.5 C 53.288,22.2148 54.9547,22.5481 56.5,23.5C 57.7653,26.7957 58.9319,30.129 60,33.5C 60.5617,26.874 64.0617,24.7073 70.5,27C 71.3366,28.0113 71.67,29.1779 71.5,30.5C 69.7376,30.6427 68.0709,30.3094 66.5,29.5C 63.8333,31.1667 63.8333,32.8333 66.5,34.5C 67.8498,33.0499 69.5165,32.3832 71.5,32.5C 70.8258,37.1514 68.1592,38.6514 63.5,37C 62.2823,35.5839 60.9489,35.7506 59.5,37.5C 57.6711,36.5354 55.8377,35.5354 54,34.5C 51.8157,34.8852 49.9823,35.8852 48.5,37.5C 47.8333,37.1667 47.1667,36.8333 46.5,36.5C 48.1805,31.8123 49.8471,27.1457 51.5,22.5 Z M 53.5,28.5 C 54.7759,29.3864 54.7759,30.3864 53.5,31.5C 52.4376,30.5754 52.4376,29.5754 53.5,28.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.718" fill="#fefffe" d="M 83.5,34.5 C 82.641,28.1855 85.3077,25.5189 91.5,26.5C 91.5,25.1667 91.5,23.8333 91.5,22.5C 92.8333,22.5 94.1667,22.5 95.5,22.5C 95.5,27.5 95.5,32.5 95.5,37.5C 92.4816,37.6646 89.4816,37.498 86.5,37C 85.3094,36.3021 84.3094,35.4687 83.5,34.5 Z M 88.5,29.5 C 91.9563,30.5362 92.2896,32.2029 89.5,34.5C 87.2957,33.218 86.9624,31.5514 88.5,29.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.737" fill="#fefffe" d="M 83.5,34.5 C 83.5,35.5 83.5,36.5 83.5,37.5C 80.242,36.9322 76.9087,36.7655 73.5,37C 72.9186,36.1074 72.5852,35.1074 72.5,34C 73.3774,31.8828 73.5441,29.7161 73,27.5C 76,26.1667 79,26.1667 82,27.5C 82.1862,30.0356 82.6862,32.369 83.5,34.5 Z M 77.5,32.5 C 79.9752,32.7467 79.9752,33.4134 77.5,34.5C 76.537,34.0302 76.537,33.3635 77.5,32.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.78" fill="#fefffe" d="M 98.5,26.5 C 100.857,26.3367 103.19,26.5034 105.5,27C 107.155,28.4443 107.822,30.2776 107.5,32.5C 104.813,32.3359 102.146,32.5026 99.5,33C 100.108,33.8699 100.941,34.3699 102,34.5C 107.553,32.6843 108.386,33.5176 104.5,37C 99.7658,38.3118 97.0991,36.6451 96.5,32C 96.3948,29.8218 97.0615,27.9885 98.5,26.5 Z"/></g>
|
||||||
|
<g><path style="opacity:0.743" fill="#fefffe" d="M 136.5,26.5 C 136.5,27.5 136.5,28.5 136.5,29.5C 134.732,32.6057 133.232,35.9391 132,39.5C 130.211,41.229 128.044,41.8957 125.5,41.5C 125.5,40.5 125.5,39.5 125.5,38.5C 126.5,38.5 127.5,38.5 128.5,38.5C 128.263,35.3527 127.43,32.3527 126,29.5C 125.503,32.146 125.336,34.8127 125.5,37.5C 124.167,37.5 122.833,37.5 121.5,37.5C 121.813,34.7531 121.48,32.0865 120.5,29.5C 119.914,29.7093 119.414,30.0426 119,30.5C 118.503,32.8098 118.337,35.1432 118.5,37.5C 117.167,37.5 115.833,37.5 114.5,37.5C 114.813,34.7531 114.48,32.0865 113.5,29.5C 112.914,29.7093 112.414,30.0426 112,30.5C 111.503,32.8098 111.337,35.1432 111.5,37.5C 110.5,37.5 109.5,37.5 108.5,37.5C 108.5,33.8333 108.5,30.1667 108.5,26.5C 115.5,26.5 122.5,26.5 129.5,26.5C 130.5,34.5 131.5,34.5 132.5,26.5C 133.833,26.5 135.167,26.5 136.5,26.5 Z"/></g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.6 KiB |
8
StudioProjects/yimaru_app/assets/icons/mascot.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<svg width="170" height="196" viewBox="0 0 170 196" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M117.067 182.177C115.877 197.212 110.275 195.264 85.2333 195.212C84.4448 195.21 75.1908 196.597 70.8903 191.049C65.6205 184.25 72.0278 153.148 66.6476 148.541C35.4189 121.795 4.51062 115.662 0.0823302 67.1799C-1.67884 47.8957 25.4175 31.9131 23.7554 61.6575C23.719 62.3058 22.9739 65.1635 25.0985 62.8067C29.2141 58.2412 41.0922 63.3382 31.2926 74.5349C28.6322 77.5748 24.8148 77.7486 29.7086 86.6516C31.7965 90.4499 32.1575 90.3838 32.4261 90.1526C41.4869 82.3661 45.6132 74.0124 53.4047 80.4715C54.0587 81.0137 84.7318 107.292 87.455 109.625C94.8854 115.991 95.9815 110.8 108.517 100.555C133.126 80.4434 138.456 71.3292 144.851 76.8954C171.122 99.7616 171.024 101.369 169.414 104.755C166.698 110.466 118.649 145.024 117.265 151.52C117.004 152.747 117.06 155.903 117.067 182.176V182.177Z" fill="#9E2891"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M95.9691 7.04231C107.402 12.0196 133.385 46.145 132.81 49.3317C132.545 50.7992 131.661 50.4639 115.134 43.4362C88.5694 32.1406 77.2952 56.2361 54.3034 49.0066C52.8236 48.5409 50.3187 46.5017 62.8984 31.6198C77.3155 14.5642 81.6895 4.59241 95.9691 7.04231Z" fill="#9E2891"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M104.13 80.7417C101.825 93.3925 78.112 87.9719 81.2646 79.2604C83.4853 73.1244 89.8626 86.8841 97.1122 80.5444C97.7996 79.9435 102.375 73.8938 104.13 80.7417Z" fill="#9E2891"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M67.3682 66.1875C67.5867 65.3843 69.8818 56.9538 76.5731 61.3381C85.3369 67.0807 70.2095 79.6629 67.3682 66.1875Z" fill="#9E2891"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M106.098 66.1791C106.299 65.3769 108.584 56.2798 115.233 61.3404C123.931 67.9593 108.563 79.161 106.098 66.1791Z" fill="#9E2891"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M89.5097 3.27682C89.8473 3.71664 89.8388 3.7058 90.174 4.14097C91.8303 6.28896 92.274 4.5622 94.3639 1.95891C96.0729 -0.16895 96.6436 -0.723371 98.6653 1.09553C100.365 2.62483 99.0602 3.86144 97.4946 5.84759C96.4128 7.21893 96.3423 7.28397 96.4213 7.96074C96.4988 8.62279 96.7775 8.4323 98.8574 9.274C105.076 11.7913 99.5535 16.8431 88.6765 15.9038C82.979 15.4121 79.9885 12.8584 81.4551 10.8831C83.1269 8.63131 86.2172 9.03241 86.4046 7.96926C86.5788 6.97811 85.8719 6.46241 85.0635 5.44804C82.9534 2.79983 82.852 2.17959 84.6577 0.764118C86.7856 -0.903788 87.5932 0.850068 89.5097 3.27682Z" fill="#9E2891"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="114" height="115" viewBox="0 0 114 115" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M108.361 64.9546C111.363 65.9012 113.03 69.1024 112.083 72.1048L110.663 76.6103L108.759 81.2049L106.463 85.616L103.791 89.8103L100.764 93.7557L97.404 97.4222L93.7374 100.782L89.792 103.809L85.5977 106.481L81.4073 108.663C78.615 110.116 75.173 109.031 73.7194 106.239C72.2658 103.447 73.3511 100.005 76.1434 98.5509L79.8923 96.5994L83.247 94.4622L86.4027 92.0408L89.3353 89.3536L92.0225 86.4209L94.4439 83.2653L96.5811 79.9106L98.4177 76.3824L99.9399 72.7076L101.211 68.6767C102.157 65.6744 105.359 64.0079 108.361 64.9546Z" fill="#9E2891"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M108.851 53.7714C105.777 54.4527 102.733 52.5136 102.052 49.4402L101.137 45.3139L99.9412 41.5204L98.4191 37.8456L96.5824 34.3174L94.4452 30.9627L92.0238 27.807L89.3366 24.8744L86.404 22.1872L83.2483 19.7658L79.8936 17.6286L76.3654 15.7919L72.6906 14.2698L68.8971 13.0737L65.0138 12.2128L61.0702 11.6936L57.0963 11.5201L53.1225 11.6936L49.1789 12.2128L45.2956 13.0737L41.5021 14.2698L37.8272 15.792L34.2991 17.6286L30.9444 19.7658L27.7887 22.1872L24.8561 24.8744L22.1689 27.807L19.7475 30.9627L17.6103 34.3173L15.7736 37.8456L14.2515 41.5203L13.0554 45.3139L12.1945 49.1972L11.6753 53.1408L11.5018 57.1147L11.6753 61.0885L12.1945 65.0321L13.0554 68.9154L14.2515 72.7089L15.7736 76.3837L17.6103 79.912L19.7475 83.2666L22.1689 86.4223L24.8561 89.3549L27.7887 92.0421L30.9443 94.4635L34.2991 96.6008L37.8273 98.4374L41.502 99.9595L45.2956 101.156L49.1789 102.017L53.1225 102.536L57.345 102.72C60.49 102.857 62.9282 105.518 62.7909 108.663C62.6536 111.808 59.9927 114.247 56.8477 114.109L52.128 113.903L47.1975 113.254L42.3423 112.178L37.5993 110.682L33.0048 108.779L28.5936 106.483L24.3994 103.811L20.454 100.783L16.7874 97.4236L13.4277 93.7571L10.4002 89.8116L7.7282 85.6174L5.43189 81.2062L3.52878 76.6117L2.03334 71.8687L0.956956 67.0136L0.307837 62.083L0.0909175 57.1147L0.307857 52.1463L0.95697 47.2158L2.03335 42.3606L3.52876 37.6177L5.43188 33.0231L7.7282 28.6119L10.4002 24.4177L13.4277 20.4723L16.7874 16.8057L20.454 13.446L24.3994 10.4185L28.5936 7.7465L33.0048 5.45019L37.5993 3.54707L42.3423 2.05164L47.1975 0.975266L52.128 0.326146L57.0964 0.109224L62.0647 0.326146L66.9952 0.975265L71.8504 2.05164L76.5934 3.54707L81.1879 5.45019L85.5991 7.74651L89.7933 10.4185L93.7387 13.446L97.4053 16.8057L100.765 20.4723L103.792 24.4177L106.465 28.6119L108.761 33.0231L110.664 37.6177L112.159 42.3606L113.182 46.9728C113.863 50.0462 111.924 53.09 108.851 53.7714Z" fill="#9E2891" fill-opacity="0.2"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
8
StudioProjects/yimaru_app/assets/icons/question.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M63.9919 0C60.8419 0 57.6937 1.20941 55.3022 3.60086L50.6045 8.29848C46.3616 12.5413 40.6045 14.9155 34.6041 14.9155H27.1926C20.4286 14.9155 14.902 20.4419 14.902 27.206V32.6496V34.6214C14.902 40.6217 12.5277 46.3747 8.28487 50.6175L3.58719 55.3152C-1.19573 60.0981 -1.19573 67.9196 3.58719 72.7025L8.28487 77.4001C12.5277 81.643 14.902 87.3919 14.902 93.3922V100.808C14.902 107.572 20.4286 113.098 27.1926 113.098H34.6041C40.6045 113.098 46.3616 115.476 50.6045 119.719L55.3022 124.413C60.0851 129.196 67.8988 129.196 72.6817 124.413L77.3834 119.719C81.6263 115.476 87.3795 113.098 93.3798 113.098H100.791C107.555 113.098 113.082 107.572 113.082 100.808V93.3922C113.082 87.3919 115.476 81.643 119.719 77.4001L124.413 72.7025C129.196 67.9196 129.196 60.0981 124.413 55.3152L119.719 50.6175C115.476 46.3746 113.082 40.6217 113.082 34.6214V27.206C113.082 20.4419 107.555 14.9155 100.791 14.9155H93.3798C87.3795 14.9155 81.6263 12.5414 77.3834 8.29848L72.6817 3.60086C70.2902 1.20941 67.142 7.80299e-06 63.9919 0Z" fill="#9E2891" fill-opacity="0.2"/>
|
||||||
|
<path d="M63.9929 8C61.2366 8 58.482 9.05824 56.3894 11.1508L52.2789 15.2612C48.5664 18.9737 43.5289 21.051 38.2786 21.051H31.7936C25.875 21.051 21.0393 25.8867 21.0393 31.8052V36.5684V38.2937C21.0393 43.544 18.9618 48.5778 15.2493 52.2903L11.1388 56.4008C6.95374 60.5858 6.95374 67.4297 11.1388 71.6147L15.2493 75.7251C18.9618 79.4376 21.0393 84.4679 21.0393 89.7182V96.2067C21.0393 102.125 25.875 106.961 31.7936 106.961H38.2786C43.5289 106.961 48.5664 109.042 52.2789 112.754L56.3894 116.861C60.5745 121.046 67.4115 121.046 71.5965 116.861L75.7105 112.754C79.423 109.042 84.457 106.961 89.7073 106.961H96.1924C102.111 106.961 106.947 102.125 106.947 96.2067V89.7182C106.947 84.4679 109.042 79.4376 112.754 75.7251L116.861 71.6147C121.046 67.4297 121.046 60.5859 116.861 56.4008L112.754 52.2903C109.042 48.5778 106.947 43.544 106.947 38.2937V31.8052C106.947 25.8867 102.111 21.051 96.1924 21.051H89.7073C84.457 21.051 79.423 18.9737 75.7105 15.2612L71.5965 11.1508C69.504 9.05823 66.7492 8.00001 63.9929 8Z" fill="#9E2891"/>
|
||||||
|
<path d="M56.417 54.2498C56.417 52.056 58.3571 49.9165 60.7503 49.9165C63.1436 49.9165 65.0837 51.6949 65.0837 53.8887C65.0837 54.6795 64.8317 55.4163 64.397 56.0353C63.102 57.8802 60.7503 59.6394 60.7503 61.8332" stroke="white" stroke-width="3.25" stroke-linecap="round"/>
|
||||||
|
<path d="M60.75 67.25H60.7695" stroke="white" stroke-width="3.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M55.333 80.25C57.6088 82.1344 60.3478 83.3349 63.4873 83.5407C65.9609 83.703 68.5434 83.7028 71.0121 83.5407C71.8621 83.4851 72.7888 83.2848 73.5867 82.9605C74.4744 82.5997 74.9186 82.4193 75.1444 82.4466C75.3699 82.4739 75.6973 82.7122 76.3521 83.1887C77.5065 84.0287 78.9607 84.6321 81.1174 84.5803C82.2079 84.5541 82.7533 84.5409 82.9972 84.1303C83.2414 83.7195 82.9374 83.151 82.3292 82.0139C81.486 80.4368 80.9517 78.6313 81.7614 77.1846C83.1558 75.1182 84.3401 72.6712 84.5132 70.0283C84.6062 68.6083 84.6062 67.1375 84.5132 65.7175C84.398 63.9564 83.9705 62.2825 83.2856 60.75" stroke="white" stroke-width="3.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M64.5127 74.8739C71.7633 74.3983 77.5387 68.6181 78.0139 61.3615C78.1068 59.9415 78.1068 58.4707 78.0139 57.0506C77.5387 49.794 71.7633 44.0137 64.5127 43.5381C62.0391 43.3758 59.4565 43.3761 56.988 43.5381C49.7374 44.0137 43.962 49.794 43.4867 57.0506C43.3937 58.4707 43.3937 59.9415 43.4867 61.3615C43.6598 64.0044 44.8443 66.4514 46.2387 68.5178C47.0483 69.9645 46.514 71.7699 45.6707 73.3471C45.0626 74.4841 44.7586 75.0527 45.0027 75.4635C45.2468 75.874 45.7921 75.8873 46.8827 75.9135C49.0393 75.9653 50.4937 75.3618 51.648 74.5218C52.3028 74.0454 52.6301 73.807 52.8558 73.7797C53.0814 73.7524 53.5254 73.9329 54.4133 74.2937C55.2113 74.618 56.1379 74.8182 56.988 74.8739C59.4565 75.036 62.0391 75.0362 64.5127 74.8739Z" stroke="white" stroke-width="3.25" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
5
StudioProjects/yimaru_app/assets/icons/success.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M63.9919 0C60.8419 0 57.6937 1.20941 55.3022 3.60086L50.6045 8.29848C46.3616 12.5413 40.6045 14.9155 34.6041 14.9155H27.1926C20.4286 14.9155 14.902 20.4419 14.902 27.206V32.6496V34.6214C14.902 40.6217 12.5277 46.3747 8.28487 50.6175L3.58719 55.3152C-1.19573 60.0981 -1.19573 67.9196 3.58719 72.7025L8.28487 77.4001C12.5277 81.643 14.902 87.3919 14.902 93.3922V100.808C14.902 107.572 20.4286 113.098 27.1926 113.098H34.6041C40.6045 113.098 46.3616 115.476 50.6045 119.719L55.3022 124.413C60.0851 129.196 67.8988 129.196 72.6817 124.413L77.3834 119.719C81.6263 115.476 87.3795 113.098 93.3798 113.098H100.791C107.555 113.098 113.082 107.572 113.082 100.808V93.3922C113.082 87.3919 115.476 81.643 119.719 77.4001L124.413 72.7025C129.196 67.9196 129.196 60.0981 124.413 55.3152L119.719 50.6175C115.476 46.3746 113.082 40.6217 113.082 34.6214V27.206C113.082 20.4419 107.555 14.9155 100.791 14.9155H93.3798C87.3795 14.9155 81.6263 12.5414 77.3834 8.29848L72.6817 3.60086C70.2902 1.20941 67.142 7.80299e-06 63.9919 0Z" fill="#9E2891" fill-opacity="0.2"/>
|
||||||
|
<path d="M63.9929 8.00006C61.2366 8.00006 58.482 9.0583 56.3894 11.1508L52.2789 15.2612C48.5664 18.9737 43.5289 21.0511 38.2786 21.0511H31.7936C25.875 21.0511 21.0393 25.8867 21.0393 31.8053V36.5684V38.2938C21.0393 43.5441 18.9618 48.5779 15.2493 52.2904L11.1388 56.4009C6.95374 60.5859 6.95374 67.4297 11.1388 71.6148L15.2493 75.7252C18.9618 79.4377 21.0393 84.468 21.0393 89.7183V96.2068C21.0393 102.125 25.875 106.961 31.7936 106.961H38.2786C43.5289 106.961 48.5664 109.042 52.2789 112.754L56.3894 116.861C60.5745 121.046 67.4115 121.046 71.5965 116.861L75.7105 112.754C79.423 109.042 84.457 106.961 89.7073 106.961H96.1924C102.111 106.961 106.947 102.125 106.947 96.2068V89.7183C106.947 84.468 109.042 79.4377 112.754 75.7252L116.861 71.6148C121.046 67.4297 121.046 60.5859 116.861 56.4009L112.754 52.2904C109.042 48.5779 106.947 43.5441 106.947 38.2938V31.8053C106.947 25.8867 102.111 21.0511 96.1924 21.0511H89.7073C84.457 21.0511 79.423 18.9738 75.7105 15.2612L71.5965 11.1508C69.504 9.05829 66.7492 8.00007 63.9929 8.00006Z" fill="#9E2891"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M81.9362 50.3204C82.8406 51.1118 82.9227 52.4904 82.1182 53.3832L59.4729 78.5182C58.6643 79.4157 57.2758 79.4727 56.3963 78.6448L43.2922 66.3084C42.475 65.5391 42.3968 64.2571 43.0979 63.3807C43.8716 62.4136 45.3123 62.2805 46.233 63.1087L56.4 72.2537C57.2836 73.0485 58.6439 72.9774 59.4399 72.095L78.9182 50.4986C79.7063 49.6246 81.0506 49.5454 81.9362 50.3204Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
BIN
StudioProjects/yimaru_app/assets/images/onboarding_1.png
Normal file
|
After Width: | Height: | Size: 448 KiB |
BIN
StudioProjects/yimaru_app/assets/images/onboarding_2.png
Normal file
|
After Width: | Height: | Size: 467 KiB |
BIN
StudioProjects/yimaru_app/assets/images/onboarding_3.png
Normal file
|
After Width: | Height: | Size: 442 KiB |
|
|
@ -1,17 +1,18 @@
|
||||||
import 'package:yimaru_app/ui/bottom_sheets/notice/notice_sheet.dart';
|
import 'package:yimaru_app/ui/bottom_sheets/notice/notice_sheet.dart';
|
||||||
import 'package:yimaru_app/ui/dialogs/info_alert/info_alert_dialog.dart';
|
import 'package:yimaru_app/ui/dialogs/info_alert/info_alert_dialog.dart';
|
||||||
import 'package:yimaru_app/ui/views/home/home_view.dart';
|
import 'package:yimaru_app/ui/views/home/home_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/startup/startup_view.dart';
|
|
||||||
import 'package:stacked/stacked_annotations.dart';
|
import 'package:stacked/stacked_annotations.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/startup/startup_view.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/language_selector.dart';
|
||||||
// @stacked-import
|
// @stacked-import
|
||||||
|
|
||||||
@StackedApp(
|
@StackedApp(
|
||||||
routes: [
|
routes: [
|
||||||
MaterialRoute(page: HomeView),
|
MaterialRoute(page: HomeView),
|
||||||
MaterialRoute(page: StartupView),
|
|
||||||
MaterialRoute(page: OnboardingView),
|
MaterialRoute(page: OnboardingView),
|
||||||
|
MaterialRoute(page: StartupView),
|
||||||
// @stacked-route
|
// @stacked-route
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,28 @@
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||||
import 'package:flutter/material.dart' as _i5;
|
import 'package:flutter/material.dart' as _i6;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart' as _i1;
|
import 'package:stacked/stacked.dart' as _i1;
|
||||||
import 'package:stacked_services/stacked_services.dart' as _i6;
|
import 'package:stacked_services/stacked_services.dart' as _i7;
|
||||||
import 'package:yimaru_app/ui/views/home/home_view.dart' as _i2;
|
import 'package:yimaru_app/ui/views/home/home_view.dart' as _i2;
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart' as _i4;
|
import 'package:yimaru_app/ui/views/onboarding/screens/language_selector.dart' as _i5;
|
||||||
import 'package:yimaru_app/ui/views/startup/startup_view.dart' as _i3;
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart' as _i3;
|
||||||
|
import 'package:yimaru_app/ui/views/startup/startup_view.dart' as _i4;
|
||||||
|
|
||||||
class Routes {
|
class Routes {
|
||||||
static const homeView = '/home-view';
|
static const homeView = '/home-view';
|
||||||
|
|
||||||
|
static const onboardingView = '/onboarding-view';
|
||||||
|
|
||||||
static const startupView = '/startup-view';
|
static const startupView = '/startup-view';
|
||||||
|
|
||||||
static const onboardingView = '/onboarding-view';
|
|
||||||
|
|
||||||
static const all = <String>{
|
static const all = <String>{
|
||||||
homeView,
|
homeView,
|
||||||
startupView,
|
|
||||||
onboardingView,
|
onboardingView,
|
||||||
|
startupView,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,31 +37,38 @@ class StackedRouter extends _i1.RouterBase {
|
||||||
page: _i2.HomeView,
|
page: _i2.HomeView,
|
||||||
),
|
),
|
||||||
_i1.RouteDef(
|
_i1.RouteDef(
|
||||||
Routes.startupView,
|
Routes.onboardingView,
|
||||||
page: _i3.StartupView,
|
page: _i3.OnboardingView,
|
||||||
),
|
),
|
||||||
_i1.RouteDef(
|
_i1.RouteDef(
|
||||||
Routes.onboardingView,
|
Routes.startupView,
|
||||||
page: _i4.OnboardingView,
|
page: _i4.StartupView,
|
||||||
),
|
),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
final _pagesMap = <Type, _i1.StackedRouteFactory>{
|
final _pagesMap = <Type, _i1.StackedRouteFactory>{
|
||||||
_i2.HomeView: (data) {
|
_i2.HomeView: (data) {
|
||||||
return _i5.MaterialPageRoute<dynamic>(
|
return _i6.MaterialPageRoute<dynamic>(
|
||||||
builder: (context) => const _i2.HomeView(),
|
builder: (context) => const _i2.HomeView(),
|
||||||
settings: data,
|
settings: data,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_i3.StartupView: (data) {
|
_i3.OnboardingView: (data) {
|
||||||
return _i5.MaterialPageRoute<dynamic>(
|
return _i6.MaterialPageRoute<dynamic>(
|
||||||
builder: (context) => const _i3.StartupView(),
|
builder: (context) => const _i3.OnboardingView(),
|
||||||
settings: data,
|
settings: data,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_i4.OnboardingView: (data) {
|
_i4.StartupView: (data) {
|
||||||
return _i5.MaterialPageRoute<dynamic>(
|
return _i6.MaterialPageRoute<dynamic>(
|
||||||
builder: (context) => const _i4.OnboardingView(),
|
builder: (context) => const _i4.StartupView(),
|
||||||
|
settings: data,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
_i5.LanguageSelector: (data) {
|
||||||
|
return _i6.MaterialPageRoute<dynamic>(
|
||||||
|
builder: (context) => const _i5.LanguageSelector(),
|
||||||
settings: data,
|
settings: data,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -71,7 +81,7 @@ class StackedRouter extends _i1.RouterBase {
|
||||||
Map<Type, _i1.StackedRouteFactory> get pagesMap => _pagesMap;
|
Map<Type, _i1.StackedRouteFactory> get pagesMap => _pagesMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NavigatorStateExtension on _i6.NavigationService {
|
extension NavigatorStateExtension on _i7.NavigationService {
|
||||||
Future<dynamic> navigateToHomeView([
|
Future<dynamic> navigateToHomeView([
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
|
|
@ -86,20 +96,6 @@ extension NavigatorStateExtension on _i6.NavigationService {
|
||||||
transition: transition);
|
transition: transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> navigateToStartupView([
|
|
||||||
int? routerId,
|
|
||||||
bool preventDuplicates = true,
|
|
||||||
Map<String, String>? parameters,
|
|
||||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
|
||||||
transition,
|
|
||||||
]) async {
|
|
||||||
return navigateTo<dynamic>(Routes.startupView,
|
|
||||||
id: routerId,
|
|
||||||
preventDuplicates: preventDuplicates,
|
|
||||||
parameters: parameters,
|
|
||||||
transition: transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<dynamic> navigateToOnboardingView([
|
Future<dynamic> navigateToOnboardingView([
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
|
|
@ -114,6 +110,22 @@ extension NavigatorStateExtension on _i6.NavigationService {
|
||||||
transition: transition);
|
transition: transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<dynamic> navigateToStartupView([
|
||||||
|
int? routerId,
|
||||||
|
bool preventDuplicates = true,
|
||||||
|
Map<String, String>? parameters,
|
||||||
|
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||||
|
transition,
|
||||||
|
]) async {
|
||||||
|
return navigateTo<dynamic>(Routes.startupView,
|
||||||
|
id: routerId,
|
||||||
|
preventDuplicates: preventDuplicates,
|
||||||
|
parameters: parameters,
|
||||||
|
transition: transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<dynamic> replaceWithHomeView([
|
Future<dynamic> replaceWithHomeView([
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
|
|
@ -128,6 +140,20 @@ extension NavigatorStateExtension on _i6.NavigationService {
|
||||||
transition: transition);
|
transition: transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<dynamic> replaceWithOnboardingView([
|
||||||
|
int? routerId,
|
||||||
|
bool preventDuplicates = true,
|
||||||
|
Map<String, String>? parameters,
|
||||||
|
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||||
|
transition,
|
||||||
|
]) async {
|
||||||
|
return replaceWith<dynamic>(Routes.onboardingView,
|
||||||
|
id: routerId,
|
||||||
|
preventDuplicates: preventDuplicates,
|
||||||
|
parameters: parameters,
|
||||||
|
transition: transition);
|
||||||
|
}
|
||||||
|
|
||||||
Future<dynamic> replaceWithStartupView([
|
Future<dynamic> replaceWithStartupView([
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
|
|
@ -142,17 +168,5 @@ extension NavigatorStateExtension on _i6.NavigationService {
|
||||||
transition: transition);
|
transition: transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> replaceWithOnboardingView([
|
|
||||||
int? routerId,
|
|
||||||
bool preventDuplicates = true,
|
|
||||||
Map<String, String>? parameters,
|
|
||||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
|
||||||
transition,
|
|
||||||
]) async {
|
|
||||||
return replaceWith<dynamic>(Routes.onboardingView,
|
|
||||||
id: routerId,
|
|
||||||
preventDuplicates: preventDuplicates,
|
|
||||||
parameters: parameters,
|
|
||||||
transition: transition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ class MainApp extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
initialRoute: Routes.startupView,
|
initialRoute: Routes.startupView,
|
||||||
|
theme: ThemeData(fontFamily: 'Aeonik'),
|
||||||
onGenerateRoute: StackedRouter().onGenerateRoute,
|
onGenerateRoute: StackedRouter().onGenerateRoute,
|
||||||
navigatorKey: StackedService.navigatorKey,
|
navigatorKey: StackedService.navigatorKey,
|
||||||
navigatorObservers: [StackedService.routeObserver],
|
navigatorObservers: [StackedService.routeObserver],
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,47 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked/stacked_annotations.dart';
|
import 'package:stacked/stacked_annotations.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/forms/age_group_form.dart';
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/assessment_completion.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/forms/country_region_form.dart';
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/assessment_failure.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/forms/educational_background_form.dart';
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/assessment_intro.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/forms/full_name_form.dart';
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/assessment_result.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/forms/learning_goal_form.dart';
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/first_assessment_form.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/forms/learning_reason_form.dart';
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/fourth_assessment_form.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/forms/occupation_form.dart';
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/result_analysis.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/retake_assessment.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/second_assessment_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/start_lesson.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/assessment/third_assessment_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/age_group_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/challenge_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/country_region_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/educational_background_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/full_name_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/learning_goal_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/learning_reason_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/occupation_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/forms/topic_form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/language_selector.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/welcome/first_welcome.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/welcome/second_welcome.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/screens/welcome/third_welcome.dart';
|
||||||
|
|
||||||
import '../../common/validators/onboarding_form_validator.dart';
|
import '../../common/validators/onboarding_form_validator.dart';
|
||||||
import 'onboarding_viewmodel.dart';
|
import 'onboarding_viewmodel.dart';
|
||||||
import 'onboarding_view.form.dart';
|
import 'onboarding_view.form.dart';
|
||||||
|
|
||||||
@FormView(fields: [
|
@FormView(fields: [
|
||||||
|
FormTextField(
|
||||||
|
name: 'answer', validator: OnboardingFormValidator.validateForm),
|
||||||
FormTextField(
|
FormTextField(
|
||||||
name: 'fullName', validator: OnboardingFormValidator.validateForm),
|
name: 'fullName', validator: OnboardingFormValidator.validateForm),
|
||||||
|
FormTextField(
|
||||||
|
name: 'challenge', validator: OnboardingFormValidator.validateForm),
|
||||||
FormTextField(
|
FormTextField(
|
||||||
name: 'occupation', validator: OnboardingFormValidator.validateForm),
|
name: 'occupation', validator: OnboardingFormValidator.validateForm),
|
||||||
FormTextField(
|
FormTextField(
|
||||||
name: 'learningReason', validator: OnboardingFormValidator.validateForm)
|
name: 'learningReason', validator: OnboardingFormValidator.validateForm),
|
||||||
|
FormTextField(name: 'topic', validator: OnboardingFormValidator.validateForm),
|
||||||
])
|
])
|
||||||
class OnboardingView extends StackedView<OnboardingViewModel>
|
class OnboardingView extends StackedView<OnboardingViewModel>
|
||||||
with $OnboardingView {
|
with $OnboardingView {
|
||||||
|
|
@ -31,42 +53,99 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
||||||
OnboardingViewModel viewModel,
|
OnboardingViewModel viewModel,
|
||||||
Widget? child,
|
Widget? child,
|
||||||
) =>
|
) =>
|
||||||
_buildOnboardingScreens(viewModel);
|
_buildOnboardingScreensWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildOnboardingScreensWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
PopScope(
|
||||||
|
canPop: false,
|
||||||
|
onPopInvokedWithResult: (value, data) => viewModel.pop(
|
||||||
|
language: viewModel.currentPage == 23 ? true : false),
|
||||||
|
child: _buildOnboardingScreens(viewModel));
|
||||||
|
|
||||||
Widget _buildOnboardingScreens(OnboardingViewModel viewModel) => IndexedStack(
|
Widget _buildOnboardingScreens(OnboardingViewModel viewModel) => IndexedStack(
|
||||||
index: viewModel.currentStep,
|
index: viewModel.currentPage,
|
||||||
children: _buildScreens(),
|
children: _buildScreens(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildScreens() => [
|
List<Widget> _buildScreens() => [
|
||||||
|
_buildFirstWelcome(),
|
||||||
|
_buildSecondWelcome(),
|
||||||
|
_buildThirdWelcome(),
|
||||||
_buildFullNameForm(),
|
_buildFullNameForm(),
|
||||||
_buildEducationalBackgroundForm(),
|
_buildEducationalBackgroundForm(),
|
||||||
_buildAgeGroupForm(),
|
_buildAgeGroupForm(),
|
||||||
_buildOccupationForm(),
|
_buildOccupationForm(),
|
||||||
_buildCountryRegionForm(),
|
_buildCountryRegionForm(),
|
||||||
_buildLearningGoalForm(),
|
_buildLearningGoalForm(),
|
||||||
_buildLearningReasonForm()
|
_buildLearningReasonForm(),
|
||||||
|
_buildChallengeForm(),
|
||||||
|
_buildTopicForm(),
|
||||||
|
_buildAssessmentIntro(),
|
||||||
|
_buildFirstAssessmentForm(),
|
||||||
|
_buildSecondAssessment(),
|
||||||
|
_buildThirdAssessment(),
|
||||||
|
_buildFourthAssessment(),
|
||||||
|
_buildAssessmentFailure(),
|
||||||
|
_buildRetakeAssessment(),
|
||||||
|
_buildResultAnalysis(),
|
||||||
|
_buildAssessmentCompletion(),
|
||||||
|
_buildAssessmentResult(),
|
||||||
|
_buildStartLesson(),
|
||||||
|
_buildLanguageSelector()
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildFullNameForm() => FullNameForm(
|
Widget _buildFirstWelcome() => const FirstWelcome();
|
||||||
fullNameController: fullNameController,
|
|
||||||
);
|
Widget _buildSecondWelcome() => const SecondWelcome();
|
||||||
|
|
||||||
|
Widget _buildThirdWelcome() => const ThirdWelcome();
|
||||||
|
|
||||||
|
Widget _buildFullNameForm() =>
|
||||||
|
FullNameForm(fullNameController: fullNameController);
|
||||||
|
|
||||||
Widget _buildEducationalBackgroundForm() => const EducationalBackgroundForm();
|
Widget _buildEducationalBackgroundForm() => const EducationalBackgroundForm();
|
||||||
|
|
||||||
Widget _buildAgeGroupForm() => const AgeGroupForm();
|
Widget _buildAgeGroupForm() => const AgeGroupForm();
|
||||||
|
|
||||||
Widget _buildOccupationForm() => OccupationForm(
|
Widget _buildOccupationForm() =>
|
||||||
occupationController: occupationController,
|
OccupationForm(occupationController: occupationController);
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildCountryRegionForm() => const CountryRegionForm();
|
Widget _buildCountryRegionForm() => const CountryRegionForm();
|
||||||
|
|
||||||
Widget _buildLearningGoalForm() => const LearningGoalForm();
|
Widget _buildLearningGoalForm() => const LearningGoalForm();
|
||||||
|
|
||||||
Widget _buildLearningReasonForm() => LearningReasonForm(
|
Widget _buildLearningReasonForm() =>
|
||||||
learningReasonController: learningReasonController,
|
LearningReasonForm(learningReasonController: learningReasonController);
|
||||||
);
|
|
||||||
|
Widget _buildChallengeForm() =>
|
||||||
|
ChallengeForm(challengeController: challengeController);
|
||||||
|
|
||||||
|
Widget _buildTopicForm() => TopicForm(topicController: topicController);
|
||||||
|
|
||||||
|
Widget _buildAssessmentIntro() => const AssessmentIntro();
|
||||||
|
|
||||||
|
Widget _buildFirstAssessmentForm() =>
|
||||||
|
FirstAssessmentForm(answerController: answerController);
|
||||||
|
|
||||||
|
Widget _buildSecondAssessment() => const SecondAssessmentForm();
|
||||||
|
|
||||||
|
Widget _buildThirdAssessment() => const ThirdAssessmentForm();
|
||||||
|
|
||||||
|
Widget _buildFourthAssessment() => const FourthAssessmentForm();
|
||||||
|
|
||||||
|
Widget _buildAssessmentFailure() => const AssessmentFailure();
|
||||||
|
|
||||||
|
Widget _buildRetakeAssessment() => const RetakeAssessment();
|
||||||
|
|
||||||
|
Widget _buildResultAnalysis() => const ResultAnalysis();
|
||||||
|
|
||||||
|
Widget _buildAssessmentCompletion() => const AssessmentCompletion();
|
||||||
|
|
||||||
|
Widget _buildAssessmentResult() => const AssessmentResult();
|
||||||
|
|
||||||
|
Widget _buildStartLesson() => const StartLesson();
|
||||||
|
|
||||||
|
Widget _buildLanguageSelector() => const LanguageSelector();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onViewModelReady(OnboardingViewModel viewModel) {
|
void onViewModelReady(OnboardingViewModel viewModel) {
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,12 @@ import 'package:yimaru_app/ui/common/validators/onboarding_form_validator.dart';
|
||||||
|
|
||||||
const bool _autoTextFieldValidation = true;
|
const bool _autoTextFieldValidation = true;
|
||||||
|
|
||||||
|
const String AnswerValueKey = 'answer';
|
||||||
const String FullNameValueKey = 'fullName';
|
const String FullNameValueKey = 'fullName';
|
||||||
|
const String ChallengeValueKey = 'challenge';
|
||||||
const String OccupationValueKey = 'occupation';
|
const String OccupationValueKey = 'occupation';
|
||||||
const String LearningReasonValueKey = 'learningReason';
|
const String LearningReasonValueKey = 'learningReason';
|
||||||
|
const String TopicValueKey = 'topic';
|
||||||
|
|
||||||
final Map<String, TextEditingController> _OnboardingViewTextEditingControllers =
|
final Map<String, TextEditingController> _OnboardingViewTextEditingControllers =
|
||||||
{};
|
{};
|
||||||
|
|
@ -22,23 +25,35 @@ final Map<String, TextEditingController> _OnboardingViewTextEditingControllers =
|
||||||
final Map<String, FocusNode> _OnboardingViewFocusNodes = {};
|
final Map<String, FocusNode> _OnboardingViewFocusNodes = {};
|
||||||
|
|
||||||
final Map<String, String? Function(String?)?> _OnboardingViewTextValidations = {
|
final Map<String, String? Function(String?)?> _OnboardingViewTextValidations = {
|
||||||
|
AnswerValueKey: OnboardingFormValidator.validateForm,
|
||||||
FullNameValueKey: OnboardingFormValidator.validateForm,
|
FullNameValueKey: OnboardingFormValidator.validateForm,
|
||||||
|
ChallengeValueKey: OnboardingFormValidator.validateForm,
|
||||||
OccupationValueKey: OnboardingFormValidator.validateForm,
|
OccupationValueKey: OnboardingFormValidator.validateForm,
|
||||||
LearningReasonValueKey: OnboardingFormValidator.validateForm,
|
LearningReasonValueKey: OnboardingFormValidator.validateForm,
|
||||||
|
TopicValueKey: OnboardingFormValidator.validateForm,
|
||||||
};
|
};
|
||||||
|
|
||||||
mixin $OnboardingView {
|
mixin $OnboardingView {
|
||||||
|
TextEditingController get answerController =>
|
||||||
|
_getFormTextEditingController(AnswerValueKey);
|
||||||
TextEditingController get fullNameController =>
|
TextEditingController get fullNameController =>
|
||||||
_getFormTextEditingController(FullNameValueKey);
|
_getFormTextEditingController(FullNameValueKey);
|
||||||
|
TextEditingController get challengeController =>
|
||||||
|
_getFormTextEditingController(ChallengeValueKey);
|
||||||
TextEditingController get occupationController =>
|
TextEditingController get occupationController =>
|
||||||
_getFormTextEditingController(OccupationValueKey);
|
_getFormTextEditingController(OccupationValueKey);
|
||||||
TextEditingController get learningReasonController =>
|
TextEditingController get learningReasonController =>
|
||||||
_getFormTextEditingController(LearningReasonValueKey);
|
_getFormTextEditingController(LearningReasonValueKey);
|
||||||
|
TextEditingController get topicController =>
|
||||||
|
_getFormTextEditingController(TopicValueKey);
|
||||||
|
|
||||||
|
FocusNode get answerFocusNode => _getFormFocusNode(AnswerValueKey);
|
||||||
FocusNode get fullNameFocusNode => _getFormFocusNode(FullNameValueKey);
|
FocusNode get fullNameFocusNode => _getFormFocusNode(FullNameValueKey);
|
||||||
|
FocusNode get challengeFocusNode => _getFormFocusNode(ChallengeValueKey);
|
||||||
FocusNode get occupationFocusNode => _getFormFocusNode(OccupationValueKey);
|
FocusNode get occupationFocusNode => _getFormFocusNode(OccupationValueKey);
|
||||||
FocusNode get learningReasonFocusNode =>
|
FocusNode get learningReasonFocusNode =>
|
||||||
_getFormFocusNode(LearningReasonValueKey);
|
_getFormFocusNode(LearningReasonValueKey);
|
||||||
|
FocusNode get topicFocusNode => _getFormFocusNode(TopicValueKey);
|
||||||
|
|
||||||
TextEditingController _getFormTextEditingController(
|
TextEditingController _getFormTextEditingController(
|
||||||
String key, {
|
String key, {
|
||||||
|
|
@ -64,9 +79,12 @@ mixin $OnboardingView {
|
||||||
/// Registers a listener on every generated controller that calls [model.setData()]
|
/// Registers a listener on every generated controller that calls [model.setData()]
|
||||||
/// with the latest textController values
|
/// with the latest textController values
|
||||||
void syncFormWithViewModel(FormStateHelper model) {
|
void syncFormWithViewModel(FormStateHelper model) {
|
||||||
|
answerController.addListener(() => _updateFormData(model));
|
||||||
fullNameController.addListener(() => _updateFormData(model));
|
fullNameController.addListener(() => _updateFormData(model));
|
||||||
|
challengeController.addListener(() => _updateFormData(model));
|
||||||
occupationController.addListener(() => _updateFormData(model));
|
occupationController.addListener(() => _updateFormData(model));
|
||||||
learningReasonController.addListener(() => _updateFormData(model));
|
learningReasonController.addListener(() => _updateFormData(model));
|
||||||
|
topicController.addListener(() => _updateFormData(model));
|
||||||
|
|
||||||
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||||
}
|
}
|
||||||
|
|
@ -78,9 +96,12 @@ mixin $OnboardingView {
|
||||||
'This feature was deprecated after 3.1.0.',
|
'This feature was deprecated after 3.1.0.',
|
||||||
)
|
)
|
||||||
void listenToFormUpdated(FormViewModel model) {
|
void listenToFormUpdated(FormViewModel model) {
|
||||||
|
answerController.addListener(() => _updateFormData(model));
|
||||||
fullNameController.addListener(() => _updateFormData(model));
|
fullNameController.addListener(() => _updateFormData(model));
|
||||||
|
challengeController.addListener(() => _updateFormData(model));
|
||||||
occupationController.addListener(() => _updateFormData(model));
|
occupationController.addListener(() => _updateFormData(model));
|
||||||
learningReasonController.addListener(() => _updateFormData(model));
|
learningReasonController.addListener(() => _updateFormData(model));
|
||||||
|
topicController.addListener(() => _updateFormData(model));
|
||||||
|
|
||||||
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||||
}
|
}
|
||||||
|
|
@ -90,9 +111,12 @@ mixin $OnboardingView {
|
||||||
model.setData(
|
model.setData(
|
||||||
model.formValueMap
|
model.formValueMap
|
||||||
..addAll({
|
..addAll({
|
||||||
|
AnswerValueKey: answerController.text,
|
||||||
FullNameValueKey: fullNameController.text,
|
FullNameValueKey: fullNameController.text,
|
||||||
|
ChallengeValueKey: challengeController.text,
|
||||||
OccupationValueKey: occupationController.text,
|
OccupationValueKey: occupationController.text,
|
||||||
LearningReasonValueKey: learningReasonController.text,
|
LearningReasonValueKey: learningReasonController.text,
|
||||||
|
TopicValueKey: topicController.text,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -134,11 +158,24 @@ extension ValueProperties on FormStateHelper {
|
||||||
return !hasAnyValidationMessage;
|
return !hasAnyValidationMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? get answerValue => this.formValueMap[AnswerValueKey] as String?;
|
||||||
String? get fullNameValue => this.formValueMap[FullNameValueKey] as String?;
|
String? get fullNameValue => this.formValueMap[FullNameValueKey] as String?;
|
||||||
|
String? get challengeValue => this.formValueMap[ChallengeValueKey] as String?;
|
||||||
String? get occupationValue =>
|
String? get occupationValue =>
|
||||||
this.formValueMap[OccupationValueKey] as String?;
|
this.formValueMap[OccupationValueKey] as String?;
|
||||||
String? get learningReasonValue =>
|
String? get learningReasonValue =>
|
||||||
this.formValueMap[LearningReasonValueKey] as String?;
|
this.formValueMap[LearningReasonValueKey] as String?;
|
||||||
|
String? get topicValue => this.formValueMap[TopicValueKey] as String?;
|
||||||
|
|
||||||
|
set answerValue(String? value) {
|
||||||
|
this.setData(
|
||||||
|
this.formValueMap..addAll({AnswerValueKey: value}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_OnboardingViewTextEditingControllers.containsKey(AnswerValueKey)) {
|
||||||
|
_OnboardingViewTextEditingControllers[AnswerValueKey]?.text = value ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
set fullNameValue(String? value) {
|
set fullNameValue(String? value) {
|
||||||
this.setData(
|
this.setData(
|
||||||
|
|
@ -151,6 +188,17 @@ extension ValueProperties on FormStateHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set challengeValue(String? value) {
|
||||||
|
this.setData(
|
||||||
|
this.formValueMap..addAll({ChallengeValueKey: value}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_OnboardingViewTextEditingControllers.containsKey(ChallengeValueKey)) {
|
||||||
|
_OnboardingViewTextEditingControllers[ChallengeValueKey]?.text =
|
||||||
|
value ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
set occupationValue(String? value) {
|
set occupationValue(String? value) {
|
||||||
this.setData(
|
this.setData(
|
||||||
this.formValueMap..addAll({OccupationValueKey: value}),
|
this.formValueMap..addAll({OccupationValueKey: value}),
|
||||||
|
|
@ -174,53 +222,96 @@ extension ValueProperties on FormStateHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set topicValue(String? value) {
|
||||||
|
this.setData(
|
||||||
|
this.formValueMap..addAll({TopicValueKey: value}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_OnboardingViewTextEditingControllers.containsKey(TopicValueKey)) {
|
||||||
|
_OnboardingViewTextEditingControllers[TopicValueKey]?.text = value ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get hasAnswer =>
|
||||||
|
this.formValueMap.containsKey(AnswerValueKey) &&
|
||||||
|
(answerValue?.isNotEmpty ?? false);
|
||||||
bool get hasFullName =>
|
bool get hasFullName =>
|
||||||
this.formValueMap.containsKey(FullNameValueKey) &&
|
this.formValueMap.containsKey(FullNameValueKey) &&
|
||||||
(fullNameValue?.isNotEmpty ?? false);
|
(fullNameValue?.isNotEmpty ?? false);
|
||||||
|
bool get hasChallenge =>
|
||||||
|
this.formValueMap.containsKey(ChallengeValueKey) &&
|
||||||
|
(challengeValue?.isNotEmpty ?? false);
|
||||||
bool get hasOccupation =>
|
bool get hasOccupation =>
|
||||||
this.formValueMap.containsKey(OccupationValueKey) &&
|
this.formValueMap.containsKey(OccupationValueKey) &&
|
||||||
(occupationValue?.isNotEmpty ?? false);
|
(occupationValue?.isNotEmpty ?? false);
|
||||||
bool get hasLearningReason =>
|
bool get hasLearningReason =>
|
||||||
this.formValueMap.containsKey(LearningReasonValueKey) &&
|
this.formValueMap.containsKey(LearningReasonValueKey) &&
|
||||||
(learningReasonValue?.isNotEmpty ?? false);
|
(learningReasonValue?.isNotEmpty ?? false);
|
||||||
|
bool get hasTopic =>
|
||||||
|
this.formValueMap.containsKey(TopicValueKey) &&
|
||||||
|
(topicValue?.isNotEmpty ?? false);
|
||||||
|
|
||||||
|
bool get hasAnswerValidationMessage =>
|
||||||
|
this.fieldsValidationMessages[AnswerValueKey]?.isNotEmpty ?? false;
|
||||||
bool get hasFullNameValidationMessage =>
|
bool get hasFullNameValidationMessage =>
|
||||||
this.fieldsValidationMessages[FullNameValueKey]?.isNotEmpty ?? false;
|
this.fieldsValidationMessages[FullNameValueKey]?.isNotEmpty ?? false;
|
||||||
|
bool get hasChallengeValidationMessage =>
|
||||||
|
this.fieldsValidationMessages[ChallengeValueKey]?.isNotEmpty ?? false;
|
||||||
bool get hasOccupationValidationMessage =>
|
bool get hasOccupationValidationMessage =>
|
||||||
this.fieldsValidationMessages[OccupationValueKey]?.isNotEmpty ?? false;
|
this.fieldsValidationMessages[OccupationValueKey]?.isNotEmpty ?? false;
|
||||||
bool get hasLearningReasonValidationMessage =>
|
bool get hasLearningReasonValidationMessage =>
|
||||||
this.fieldsValidationMessages[LearningReasonValueKey]?.isNotEmpty ??
|
this.fieldsValidationMessages[LearningReasonValueKey]?.isNotEmpty ??
|
||||||
false;
|
false;
|
||||||
|
bool get hasTopicValidationMessage =>
|
||||||
|
this.fieldsValidationMessages[TopicValueKey]?.isNotEmpty ?? false;
|
||||||
|
|
||||||
|
String? get answerValidationMessage =>
|
||||||
|
this.fieldsValidationMessages[AnswerValueKey];
|
||||||
String? get fullNameValidationMessage =>
|
String? get fullNameValidationMessage =>
|
||||||
this.fieldsValidationMessages[FullNameValueKey];
|
this.fieldsValidationMessages[FullNameValueKey];
|
||||||
|
String? get challengeValidationMessage =>
|
||||||
|
this.fieldsValidationMessages[ChallengeValueKey];
|
||||||
String? get occupationValidationMessage =>
|
String? get occupationValidationMessage =>
|
||||||
this.fieldsValidationMessages[OccupationValueKey];
|
this.fieldsValidationMessages[OccupationValueKey];
|
||||||
String? get learningReasonValidationMessage =>
|
String? get learningReasonValidationMessage =>
|
||||||
this.fieldsValidationMessages[LearningReasonValueKey];
|
this.fieldsValidationMessages[LearningReasonValueKey];
|
||||||
|
String? get topicValidationMessage =>
|
||||||
|
this.fieldsValidationMessages[TopicValueKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Methods on FormStateHelper {
|
extension Methods on FormStateHelper {
|
||||||
|
setAnswerValidationMessage(String? validationMessage) =>
|
||||||
|
this.fieldsValidationMessages[AnswerValueKey] = validationMessage;
|
||||||
setFullNameValidationMessage(String? validationMessage) =>
|
setFullNameValidationMessage(String? validationMessage) =>
|
||||||
this.fieldsValidationMessages[FullNameValueKey] = validationMessage;
|
this.fieldsValidationMessages[FullNameValueKey] = validationMessage;
|
||||||
|
setChallengeValidationMessage(String? validationMessage) =>
|
||||||
|
this.fieldsValidationMessages[ChallengeValueKey] = validationMessage;
|
||||||
setOccupationValidationMessage(String? validationMessage) =>
|
setOccupationValidationMessage(String? validationMessage) =>
|
||||||
this.fieldsValidationMessages[OccupationValueKey] = validationMessage;
|
this.fieldsValidationMessages[OccupationValueKey] = validationMessage;
|
||||||
setLearningReasonValidationMessage(String? validationMessage) =>
|
setLearningReasonValidationMessage(String? validationMessage) =>
|
||||||
this.fieldsValidationMessages[LearningReasonValueKey] = validationMessage;
|
this.fieldsValidationMessages[LearningReasonValueKey] = validationMessage;
|
||||||
|
setTopicValidationMessage(String? validationMessage) =>
|
||||||
|
this.fieldsValidationMessages[TopicValueKey] = validationMessage;
|
||||||
|
|
||||||
/// Clears text input fields on the Form
|
/// Clears text input fields on the Form
|
||||||
void clearForm() {
|
void clearForm() {
|
||||||
|
answerValue = '';
|
||||||
fullNameValue = '';
|
fullNameValue = '';
|
||||||
|
challengeValue = '';
|
||||||
occupationValue = '';
|
occupationValue = '';
|
||||||
learningReasonValue = '';
|
learningReasonValue = '';
|
||||||
|
topicValue = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates text input fields on the Form
|
/// Validates text input fields on the Form
|
||||||
void validateForm() {
|
void validateForm() {
|
||||||
this.setValidationMessages({
|
this.setValidationMessages({
|
||||||
|
AnswerValueKey: getValidationMessage(AnswerValueKey),
|
||||||
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
||||||
|
ChallengeValueKey: getValidationMessage(ChallengeValueKey),
|
||||||
OccupationValueKey: getValidationMessage(OccupationValueKey),
|
OccupationValueKey: getValidationMessage(OccupationValueKey),
|
||||||
LearningReasonValueKey: getValidationMessage(LearningReasonValueKey),
|
LearningReasonValueKey: getValidationMessage(LearningReasonValueKey),
|
||||||
|
TopicValueKey: getValidationMessage(TopicValueKey),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +331,10 @@ String? getValidationMessage(String key) {
|
||||||
/// Updates the fieldsValidationMessages on the FormViewModel
|
/// Updates the fieldsValidationMessages on the FormViewModel
|
||||||
void updateValidationData(FormStateHelper model) =>
|
void updateValidationData(FormStateHelper model) =>
|
||||||
model.setValidationMessages({
|
model.setValidationMessages({
|
||||||
|
AnswerValueKey: getValidationMessage(AnswerValueKey),
|
||||||
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
||||||
|
ChallengeValueKey: getValidationMessage(ChallengeValueKey),
|
||||||
OccupationValueKey: getValidationMessage(OccupationValueKey),
|
OccupationValueKey: getValidationMessage(OccupationValueKey),
|
||||||
LearningReasonValueKey: getValidationMessage(LearningReasonValueKey),
|
LearningReasonValueKey: getValidationMessage(LearningReasonValueKey),
|
||||||
|
TopicValueKey: getValidationMessage(TopicValueKey),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
import 'package:yimaru_app/app/app.router.dart';
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
import 'onboarding_view.form.dart';
|
import 'onboarding_view.form.dart';
|
||||||
|
|
||||||
class OnboardingViewModel extends FormViewModel {
|
class OnboardingViewModel extends FormViewModel {
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
int _currentStep = 0;
|
int _currentPage = 0;
|
||||||
|
|
||||||
int get currentStep => _currentStep;
|
int get currentPage => _currentPage;
|
||||||
|
|
||||||
|
int _previousPage = 0;
|
||||||
|
|
||||||
|
int get previousPage => _previousPage;
|
||||||
|
|
||||||
// Full name
|
// Full name
|
||||||
bool _focusFullName = false;
|
bool _focusFullName = false;
|
||||||
|
|
@ -75,9 +80,9 @@ class OnboardingViewModel extends FormViewModel {
|
||||||
List<Map<String, dynamic>> get learningGoals => _learningGoals;
|
List<Map<String, dynamic>> get learningGoals => _learningGoals;
|
||||||
|
|
||||||
// Learning reason
|
// Learning reason
|
||||||
bool _showTextBox = false;
|
bool _showReasonTextBox = false;
|
||||||
|
|
||||||
bool get showTextBox => _showTextBox;
|
bool get showReasonTextBox => _showReasonTextBox;
|
||||||
|
|
||||||
bool _focusLearningReason = false;
|
bool _focusLearningReason = false;
|
||||||
|
|
||||||
|
|
@ -97,6 +102,114 @@ class OnboardingViewModel extends FormViewModel {
|
||||||
|
|
||||||
List<String> get learningReasons => _learningReasons;
|
List<String> get learningReasons => _learningReasons;
|
||||||
|
|
||||||
|
// Challenges
|
||||||
|
bool _showChallengeTextBox = false;
|
||||||
|
|
||||||
|
bool get showChallengeTextBox => _showChallengeTextBox;
|
||||||
|
|
||||||
|
bool _focusChallenge = false;
|
||||||
|
|
||||||
|
bool get focusChallenge => _focusChallenge;
|
||||||
|
|
||||||
|
String? _selectedChallenge;
|
||||||
|
|
||||||
|
String? get selectedChallenge => _selectedChallenge;
|
||||||
|
|
||||||
|
final List<String> _challenges = [
|
||||||
|
'Pronunciation',
|
||||||
|
'Finding words or grammar quickly',
|
||||||
|
'Feeling nervous or lacking confidence',
|
||||||
|
'Understanding accents or fast speech',
|
||||||
|
'Other'
|
||||||
|
];
|
||||||
|
|
||||||
|
List<String> get challenges => _challenges;
|
||||||
|
|
||||||
|
// Topic
|
||||||
|
bool _showTopicTextBox = false;
|
||||||
|
|
||||||
|
bool get showTopicTextBox => _showTopicTextBox;
|
||||||
|
|
||||||
|
bool _focusTopic = false;
|
||||||
|
|
||||||
|
bool get focusTopic => _focusTopic;
|
||||||
|
|
||||||
|
String? _selectedTopic;
|
||||||
|
|
||||||
|
String? get selectedTopic => _selectedTopic;
|
||||||
|
|
||||||
|
final List<String> _topics = [
|
||||||
|
'Food & Cooking',
|
||||||
|
' Hobbies, Sports, Music',
|
||||||
|
'Tech, News, Business',
|
||||||
|
'Travel, Places, Culture',
|
||||||
|
'Other'
|
||||||
|
];
|
||||||
|
|
||||||
|
List<String> get topics => _topics;
|
||||||
|
|
||||||
|
// First assessment
|
||||||
|
bool _focusFirstAssessment = false;
|
||||||
|
|
||||||
|
bool get focusFirstAssessment => _focusFirstAssessment;
|
||||||
|
|
||||||
|
// Second assessment
|
||||||
|
final List<String> _secondAnswers = [
|
||||||
|
'go',
|
||||||
|
'goes',
|
||||||
|
'went',
|
||||||
|
'going',
|
||||||
|
];
|
||||||
|
|
||||||
|
List<String> get secondAnswers => _secondAnswers;
|
||||||
|
|
||||||
|
String? _selectedA2Answer;
|
||||||
|
|
||||||
|
String? get selectedA2Answer => _selectedA2Answer;
|
||||||
|
|
||||||
|
// Third assessment
|
||||||
|
final List<String> _thirdAnswers = [
|
||||||
|
'reduce',
|
||||||
|
'grow',
|
||||||
|
'stop',
|
||||||
|
'hide',
|
||||||
|
];
|
||||||
|
|
||||||
|
List<String> get thirdAnswers => _thirdAnswers;
|
||||||
|
|
||||||
|
String? _selectedA3Answer;
|
||||||
|
|
||||||
|
String? get selectedA3Answer => _selectedA3Answer;
|
||||||
|
|
||||||
|
// Third assessment
|
||||||
|
final List<String> _fourthAnswers = [
|
||||||
|
'reduce',
|
||||||
|
'grow',
|
||||||
|
'stop',
|
||||||
|
'hide',
|
||||||
|
];
|
||||||
|
|
||||||
|
List<String> get fourthAnswers => _fourthAnswers;
|
||||||
|
|
||||||
|
String? _selectedA4Answer;
|
||||||
|
|
||||||
|
String? get selectedA4Answer => _selectedA4Answer;
|
||||||
|
|
||||||
|
// Languages
|
||||||
|
final List<Map<String, dynamic>> _languages = [
|
||||||
|
{'code': 'አማ', 'language': 'አማርኛ'},
|
||||||
|
{'code': 'EN', 'language': 'English'},
|
||||||
|
];
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> get languages => _languages;
|
||||||
|
|
||||||
|
Map<String, dynamic> _selectedLanguage = {
|
||||||
|
'code': 'EN',
|
||||||
|
'language': 'English'
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, dynamic> get selectedLanguage => _selectedLanguage;
|
||||||
|
|
||||||
// Full name
|
// Full name
|
||||||
void setFullNameFocus() {
|
void setFullNameFocus() {
|
||||||
_focusFullName = true;
|
_focusFullName = true;
|
||||||
|
|
@ -150,10 +263,10 @@ class OnboardingViewModel extends FormViewModel {
|
||||||
void setSelectedLearningReason(String title) {
|
void setSelectedLearningReason(String title) {
|
||||||
_selectedLearningReason = title;
|
_selectedLearningReason = title;
|
||||||
if (title.toLowerCase() == 'other') {
|
if (title.toLowerCase() == 'other') {
|
||||||
_showTextBox = true;
|
_showReasonTextBox = true;
|
||||||
} else {
|
} else {
|
||||||
if (_showTextBox) {
|
if (_showReasonTextBox) {
|
||||||
_showTextBox = false;
|
_showReasonTextBox = false;
|
||||||
_focusLearningReason = false;
|
_focusLearningReason = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -163,13 +276,114 @@ class OnboardingViewModel extends FormViewModel {
|
||||||
bool isSelectedLearningReason(String title) =>
|
bool isSelectedLearningReason(String title) =>
|
||||||
_selectedLearningReason == title;
|
_selectedLearningReason == title;
|
||||||
|
|
||||||
void next() {
|
// Challenges
|
||||||
_currentStep++;
|
void setChallengesFocus() {
|
||||||
|
_focusChallenge = true;
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop() {
|
void setSelectedChallenge(String title) {
|
||||||
_currentStep--;
|
_selectedChallenge = title;
|
||||||
|
if (title.toLowerCase() == 'other') {
|
||||||
|
_showChallengeTextBox = true;
|
||||||
|
} else {
|
||||||
|
if (_showChallengeTextBox) {
|
||||||
|
_showChallengeTextBox = false;
|
||||||
|
_focusChallenge = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSelectedChallenge(String title) => _selectedChallenge == title;
|
||||||
|
|
||||||
|
// Topics
|
||||||
|
void setTopicsFocus() {
|
||||||
|
_focusTopic = true;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSelectedTopic(String title) {
|
||||||
|
_selectedTopic = title;
|
||||||
|
if (title.toLowerCase() == 'other') {
|
||||||
|
_showTopicTextBox = true;
|
||||||
|
} else {
|
||||||
|
if (_showTopicTextBox) {
|
||||||
|
_showTopicTextBox = false;
|
||||||
|
_focusTopic = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSelectedTopic(String title) => _selectedTopic == title;
|
||||||
|
|
||||||
|
// First assessment
|
||||||
|
void setFirstAssessmentFocus() {
|
||||||
|
_focusFirstAssessment = true;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second assessment
|
||||||
|
void setSelectedA2Answer(String title) {
|
||||||
|
_selectedA2Answer = title;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSelectedA2Answer(String title) => _selectedA2Answer == title;
|
||||||
|
|
||||||
|
// Third assessment
|
||||||
|
void setSelectedA3Answer(String title) {
|
||||||
|
_selectedA3Answer = title;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSelectedA3Answer(String title) => _selectedA3Answer == title;
|
||||||
|
|
||||||
|
// Fourth assessment
|
||||||
|
void setSelectedA4Answer(String title) {
|
||||||
|
_selectedA4Answer = title;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSelectedA4Answer(String title) => _selectedA4Answer == title;
|
||||||
|
|
||||||
|
// Language
|
||||||
|
void setSelectedLanguage(Map<String, dynamic> title) {
|
||||||
|
_selectedLanguage = title;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSelectedLanguage(String title) =>
|
||||||
|
_selectedLanguage['language'] == title;
|
||||||
|
|
||||||
|
void next({int? page}) async {
|
||||||
|
if (page == null) {
|
||||||
|
if (_previousPage != 0) {
|
||||||
|
_currentPage = _previousPage;
|
||||||
|
} else {
|
||||||
|
_currentPage++;
|
||||||
|
if (_currentPage == 19) {
|
||||||
|
rebuildUi();
|
||||||
|
await Future.delayed(const Duration(seconds: 3));
|
||||||
|
_currentPage++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_previousPage = _currentPage;
|
||||||
|
_currentPage = page;
|
||||||
|
}
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop({bool language = false}) {
|
||||||
|
if (!language) {
|
||||||
|
_currentPage--;
|
||||||
|
} else {
|
||||||
|
_currentPage = _previousPage;
|
||||||
|
_previousPage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class AssessmentCompletion extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const AssessmentCompletion({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(
|
||||||
|
showBackButton: false,
|
||||||
|
showLanguageSelection: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceLarge,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
|
'assets/icons/complete.svg',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Assessment complete!',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'We’re now analyzing your speaking skills',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'View My Results',
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class AssessmentFailure extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const AssessmentFailure({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(showBackButton: false);
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildLowerColumn(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceLarge,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset('assets/icons/alert.svg');
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'We didn’t get enough from your assessment',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'Your assessment wasn’t long enough for us to analyze your speaking level. You can retake the call to get accurate results ',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildLowerColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildLowerColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildLowerColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
_buildContinueButton(viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSkipButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Continue Assessment',
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildSkipButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Skip',
|
||||||
|
borderRadius: 12,
|
||||||
|
borderColor: kcPrimaryColor,
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
backgroundColor: kcWhiteColor,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class AssessmentIntro extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const AssessmentIntro({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildLowerColumn(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(showBackButton: false);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Want a quick assessment to know your English level?',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'Answer a few quick questions to help us understand your English proficiency.',
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildLowerColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildLowerColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildLowerColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
_buildContinueButton(viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSkipButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildSkipButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButton(OnboardingViewModel viewModel) =>
|
||||||
|
const CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Skip',
|
||||||
|
borderRadius: 12,
|
||||||
|
borderColor: kcPrimaryColor,
|
||||||
|
backgroundColor: kcWhiteColor,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class AssessmentResult extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const AssessmentResult({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(showBackButton: false);
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildLowerColumn(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceLarge,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildPrimarySubTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSecondarySubTitle()
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'You’re likely a B1 speaker!',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildPrimarySubTitle() => const Text(
|
||||||
|
'Great Job! Here’s your next step to keep improving.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset('assets/icons/b1.svg');
|
||||||
|
|
||||||
|
Widget _buildSecondarySubTitle() => const Text(
|
||||||
|
'Let\'s start your practice',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildLowerColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildLowerColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildLowerColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
_buildContinueButton(viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSkipButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildSkipButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Practice Speaking',
|
||||||
|
borderColor: kcPrimaryColor,
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
backgroundColor: kcWhiteColor,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
import '../../onboarding_view.form.dart';
|
||||||
|
|
||||||
|
class FirstAssessmentForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
final TextEditingController answerController;
|
||||||
|
|
||||||
|
const FirstAssessmentForm({super.key, required this.answerController});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceLarge,
|
||||||
|
_buildFirstAssessmentFormField(viewModel),
|
||||||
|
if (viewModel.hasAnswerValidationMessage &&
|
||||||
|
viewModel.focusFirstAssessment)
|
||||||
|
verticalSpaceTiny,
|
||||||
|
if (viewModel.hasAnswerValidationMessage &&
|
||||||
|
viewModel.focusFirstAssessment)
|
||||||
|
_buildFirstAssessmentValidatorWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(
|
||||||
|
showBackButton: false,
|
||||||
|
showLanguageSelection: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'1. What is the plural of “book”?',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildFirstAssessmentFormField(OnboardingViewModel viewModel) =>
|
||||||
|
TextFormField(
|
||||||
|
controller: answerController,
|
||||||
|
onTap: viewModel.setFirstAssessmentFocus,
|
||||||
|
decoration: inputDecoration(focus: viewModel.focusFirstAssessment),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildFirstAssessmentValidatorWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
viewModel.hasAnswerValidationMessage
|
||||||
|
? _buildFirstAssessmentValidator(viewModel)
|
||||||
|
: Container();
|
||||||
|
|
||||||
|
Widget _buildFirstAssessmentValidator(OnboardingViewModel viewModel) => Text(
|
||||||
|
viewModel.answerValidationMessage!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.red,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor:
|
||||||
|
viewModel.focusFirstAssessment && answerController.text.isNotEmpty
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
|
onTap:
|
||||||
|
viewModel.focusFirstAssessment && answerController.text.isNotEmpty
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class FourthAssessmentForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const FourthAssessmentForm({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAnswers(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(
|
||||||
|
showBackButton: false,
|
||||||
|
showLanguageSelection: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Q4. Choose the word that best matches the meaning of ‘meticulous’:',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAnswers(OnboardingViewModel viewModel) => ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: viewModel.fourthAnswers.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) => _buildAnswer(
|
||||||
|
title: viewModel.fourthAnswers[index],
|
||||||
|
selected:
|
||||||
|
viewModel.isSelectedA4Answer(viewModel.fourthAnswers[index]),
|
||||||
|
onTap: () =>
|
||||||
|
viewModel.setSelectedA4Answer(viewModel.fourthAnswers[index]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAnswer(
|
||||||
|
{required String title,
|
||||||
|
required bool selected,
|
||||||
|
required GestureTapCallback onTap}) =>
|
||||||
|
CustomSmallRadioButton(
|
||||||
|
title: title,
|
||||||
|
onTap: onTap,
|
||||||
|
selected: selected,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
onTap:
|
||||||
|
viewModel.selectedA4Answer != null ? () => viewModel.next() : null,
|
||||||
|
backgroundColor: viewModel.selectedA4Answer != null
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class ResultAnalysis extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const ResultAnalysis({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMassive,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(showBackButton: false);
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
|
'assets/icons/progress_indicator.svg',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Analyzing your results…',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'We’re now analyzing your speaking skills',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class RetakeAssessment extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const RetakeAssessment({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildLowerColumn(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceLarge,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(showBackButton: false);
|
||||||
|
|
||||||
|
Widget _buildIcon() => const Icon(
|
||||||
|
Icons.warning_amber_rounded,
|
||||||
|
size: 100,
|
||||||
|
color: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'We didn’t get enough from your assessment',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'Your assessment wasn’t long enough for us to analyze your speaking level. You can retake the call to get accurate results ',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildLowerColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildLowerColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildLowerColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
_buildContinueButton(viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSkipButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Retake Assessment',
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildSkipButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Skip',
|
||||||
|
borderRadius: 12,
|
||||||
|
borderColor: kcPrimaryColor,
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
backgroundColor: kcWhiteColor,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class SecondAssessmentForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const SecondAssessmentForm({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAnswers(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(
|
||||||
|
showBackButton: false,
|
||||||
|
showLanguageSelection: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Q2. Choose the correct word to complete the sentence:\nI ____ to school yesterday. ',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAnswers(OnboardingViewModel viewModel) => ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: viewModel.secondAnswers.length,
|
||||||
|
itemBuilder: (context, index) => _buildAnswer(
|
||||||
|
title: viewModel.secondAnswers[index],
|
||||||
|
selected:
|
||||||
|
viewModel.isSelectedA2Answer(viewModel.secondAnswers[index]),
|
||||||
|
onTap: () =>
|
||||||
|
viewModel.setSelectedA2Answer(viewModel.secondAnswers[index]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAnswer(
|
||||||
|
{required String title,
|
||||||
|
required bool selected,
|
||||||
|
required GestureTapCallback onTap}) =>
|
||||||
|
CustomSmallRadioButton(
|
||||||
|
title: title,
|
||||||
|
onTap: onTap,
|
||||||
|
selected: selected,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
onTap: viewModel.selectedA2Answer != null
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
backgroundColor: viewModel.selectedA2Answer != null
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class StartLesson extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const StartLesson({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(
|
||||||
|
showBackButton: false,
|
||||||
|
showLanguageSelection: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceLarge,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset('assets/icons/mascot.svg');
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: 'Welcome aboard',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: ', Bisrat!',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'You’re ready to explore your personalized lessons.',
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
const CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Go to My Lessons',
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class ThirdAssessmentForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const ThirdAssessmentForm({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAnswers(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(
|
||||||
|
showBackButton: false,
|
||||||
|
showLanguageSelection: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Q3. Which word means the same as ‘expand’?',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAnswers(OnboardingViewModel viewModel) => ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: viewModel.thirdAnswers.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) => _buildAnswer(
|
||||||
|
title: viewModel.thirdAnswers[index],
|
||||||
|
selected: viewModel.isSelectedA3Answer(viewModel.thirdAnswers[index]),
|
||||||
|
onTap: () =>
|
||||||
|
viewModel.setSelectedA3Answer(viewModel.thirdAnswers[index]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAnswer(
|
||||||
|
{required String title,
|
||||||
|
required bool selected,
|
||||||
|
required GestureTapCallback onTap}) =>
|
||||||
|
CustomSmallRadioButton(
|
||||||
|
title: title,
|
||||||
|
onTap: onTap,
|
||||||
|
selected: selected,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor: viewModel.selectedA3Answer != null
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
|
onTap:
|
||||||
|
viewModel.selectedA3Answer != null ? () => viewModel.next() : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/shared/widgets/custom_elevated_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/custom_small_radio_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/onboarding_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
class AgeGroupForm extends ViewModelWidget<OnboardingViewModel> {
|
class AgeGroupForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
const AgeGroupForm({super.key});
|
const AgeGroupForm({super.key});
|
||||||
|
|
@ -14,6 +14,8 @@ class AgeGroupForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
|
|
@ -72,15 +74,13 @@ class AgeGroupForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
|
||||||
Widget _buildSubTitle() => const Text(
|
Widget _buildSubTitle() => const Text(
|
||||||
'We’ll personalize your learning experience based on your age.',
|
'We’ll personalize your learning experience based on your age.',
|
||||||
style: TextStyle(
|
style: TextStyle(color: kcMediumGrey),
|
||||||
color: kcMediumGrey,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAgeGroups(OnboardingViewModel viewModel) => ListView.builder(
|
Widget _buildAgeGroups(OnboardingViewModel viewModel) => ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemCount: viewModel.ageGroups.length,
|
itemCount: viewModel.ageGroups.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) => _buildAgeGroup(
|
itemBuilder: (context, index) => _buildAgeGroup(
|
||||||
title: viewModel.ageGroups[index],
|
title: viewModel.ageGroups[index],
|
||||||
selected: viewModel.isSelectedAgeGroup(viewModel.ageGroups[index]),
|
selected: viewModel.isSelectedAgeGroup(viewModel.ageGroups[index]),
|
||||||
|
|
@ -109,8 +109,11 @@ class AgeGroupForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
onTap: () => viewModel.next(),
|
|
||||||
foregroundColor: kcWhiteColor,
|
foregroundColor: kcWhiteColor,
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: viewModel.selectedAgeGroup != null
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
|
onTap:
|
||||||
|
viewModel.selectedAgeGroup != null ? () => viewModel.next() : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class ChallengeForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
final TextEditingController challengeController;
|
||||||
|
|
||||||
|
const ChallengeForm({super.key, required this.challengeController});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildChallenges(viewModel),
|
||||||
|
if (viewModel.showChallengeTextBox) _buildChallengeFormField(viewModel),
|
||||||
|
if (viewModel.showChallengeTextBox &&
|
||||||
|
viewModel.hasChallengeValidationMessage &&
|
||||||
|
viewModel.focusChallenge)
|
||||||
|
verticalSpaceTiny,
|
||||||
|
if (viewModel.showChallengeTextBox &&
|
||||||
|
viewModel.hasChallengeValidationMessage &&
|
||||||
|
viewModel.focusChallenge)
|
||||||
|
_buildChallengeValidatorWrapper(viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar();
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'What challenge do you face most with English?',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'Everyone has struggles, let’s start fixing yours 😊',
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildChallenges(OnboardingViewModel viewModel) => ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
itemCount: viewModel.challenges.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) => _buildChallenge(
|
||||||
|
title: viewModel.challenges[index],
|
||||||
|
onTap: () =>
|
||||||
|
viewModel.setSelectedChallenge(viewModel.challenges[index]),
|
||||||
|
selected: viewModel.isSelectedChallenge(viewModel.challenges[index]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildChallenge(
|
||||||
|
{required String title,
|
||||||
|
required bool selected,
|
||||||
|
required GestureTapCallback onTap}) =>
|
||||||
|
CustomSmallRadioButton(
|
||||||
|
title: title,
|
||||||
|
onTap: onTap,
|
||||||
|
selected: selected,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildChallengeFormField(OnboardingViewModel viewModel) =>
|
||||||
|
TextFormField(
|
||||||
|
maxLines: 3,
|
||||||
|
controller: challengeController,
|
||||||
|
onTap: viewModel.setChallengesFocus,
|
||||||
|
decoration: inputDecoration(focus: true, hint: 'Write your challenge…'),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildChallengeValidatorWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
viewModel.hasChallengeValidationMessage
|
||||||
|
? _buildChallengeValidator(viewModel)
|
||||||
|
: Container();
|
||||||
|
|
||||||
|
Widget _buildChallengeValidator(OnboardingViewModel viewModel) => Text(
|
||||||
|
viewModel.challengeValidationMessage!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.red,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
onTap: viewModel.selectedChallenge != null
|
||||||
|
? viewModel.selectedChallenge?.toLowerCase() == 'other'
|
||||||
|
? viewModel.focusChallenge
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null
|
||||||
|
: () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
backgroundColor: viewModel.selectedChallenge != null
|
||||||
|
? viewModel.selectedChallenge?.toLowerCase() == 'other'
|
||||||
|
? viewModel.focusChallenge &&
|
||||||
|
challengeController.text.isNotEmpty
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2)
|
||||||
|
: kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2));
|
||||||
|
}
|
||||||
|
|
@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/shared/widgets/custom_elevated_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/custom_dropdown.dart';
|
import 'package:yimaru_app/ui/widgets/custom_dropdown.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/onboarding_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
class CountryRegionForm extends ViewModelWidget<OnboardingViewModel> {
|
class CountryRegionForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
const CountryRegionForm({super.key});
|
const CountryRegionForm({super.key});
|
||||||
|
|
@ -75,26 +75,26 @@ class CountryRegionForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
|
||||||
Widget _buildSubTitle() => const Text(
|
Widget _buildSubTitle() => const Text(
|
||||||
'Select your country and region from the dropdown',
|
'Select your country and region from the dropdown',
|
||||||
style: TextStyle(
|
style: TextStyle(color: kcMediumGrey),
|
||||||
color: kcMediumGrey,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildCountryDropDown(OnboardingViewModel viewModel) =>
|
Widget _buildCountryDropDown(OnboardingViewModel viewModel) =>
|
||||||
CustomDropDownPicker(
|
CustomDropDownPicker(
|
||||||
|
onChanged: (value) {},
|
||||||
hint: 'Select country',
|
hint: 'Select country',
|
||||||
icon: _buildSearchIcon(),
|
icon: _buildSearchIcon(),
|
||||||
|
selectedItem: 'Ethiopia',
|
||||||
items: (value, props) => viewModel.getCountries(),
|
items: (value, props) => viewModel.getCountries(),
|
||||||
onChanged: (value) {},
|
);
|
||||||
selectedItem: 'Ethiopia');
|
|
||||||
|
|
||||||
Widget _buildRegionDropDown(OnboardingViewModel viewModel) =>
|
Widget _buildRegionDropDown(OnboardingViewModel viewModel) =>
|
||||||
CustomDropDownPicker(
|
CustomDropDownPicker(
|
||||||
hint: 'Select region',
|
hint: 'Select region',
|
||||||
icon: _buildSearchIcon(),
|
|
||||||
items: (value, props) => viewModel.getRegions('Addis Ababa'),
|
|
||||||
onChanged: (value) {},
|
onChanged: (value) {},
|
||||||
selectedItem: 'Addis Ababa');
|
icon: _buildSearchIcon(),
|
||||||
|
selectedItem: 'Addis Ababa',
|
||||||
|
items: (value, props) => viewModel.getRegions('Addis Ababa'),
|
||||||
|
);
|
||||||
|
|
||||||
Icon _buildSearchIcon() => const Icon(
|
Icon _buildSearchIcon() => const Icon(
|
||||||
Icons.search,
|
Icons.search,
|
||||||
|
|
@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/shared/widgets/custom_elevated_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/custom_small_radio_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/onboarding_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
class EducationalBackgroundForm extends ViewModelWidget<OnboardingViewModel> {
|
class EducationalBackgroundForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
const EducationalBackgroundForm({super.key});
|
const EducationalBackgroundForm({super.key});
|
||||||
|
|
@ -14,6 +14,7 @@ class EducationalBackgroundForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
|
|
@ -72,9 +73,7 @@ class EducationalBackgroundForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
|
||||||
Widget _buildSubTitle() => const Text(
|
Widget _buildSubTitle() => const Text(
|
||||||
'This helps us tailor your lessons to your experience.',
|
'This helps us tailor your lessons to your experience.',
|
||||||
style: TextStyle(
|
style: TextStyle(color: kcMediumGrey),
|
||||||
color: kcMediumGrey,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildEducationalLevels(OnboardingViewModel viewModel) =>
|
Widget _buildEducationalLevels(OnboardingViewModel viewModel) =>
|
||||||
|
|
@ -111,8 +110,12 @@ class EducationalBackgroundForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
onTap: () => viewModel.next(),
|
|
||||||
foregroundColor: kcWhiteColor,
|
foregroundColor: kcWhiteColor,
|
||||||
backgroundColor: kcPrimaryColor,
|
onTap: viewModel.selectedEducationalBackground != null
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
backgroundColor: viewModel.selectedEducationalBackground != null
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/shared/widgets/custom_elevated_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/onboarding_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
import '../onboarding_view.form.dart';
|
import '../../onboarding_view.form.dart';
|
||||||
|
|
||||||
class FullNameForm extends ViewModelWidget<OnboardingViewModel> {
|
class FullNameForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
final TextEditingController fullNameController;
|
final TextEditingController fullNameController;
|
||||||
|
|
@ -66,9 +66,7 @@ class FullNameForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
_buildFullNameValidatorWrapper(viewModel)
|
_buildFullNameValidatorWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBar() => const OnboardingAppBar(
|
Widget _buildAppBar() => const OnboardingAppBar(showBackButton: false);
|
||||||
showBackButton: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildTitle() => const Text(
|
Widget _buildTitle() => const Text(
|
||||||
'What should we call you? 😊',
|
'What should we call you? 😊',
|
||||||
|
|
@ -81,17 +79,14 @@ class FullNameForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
|
||||||
Widget _buildSubTitle() => const Text(
|
Widget _buildSubTitle() => const Text(
|
||||||
'We’ll use your name to personalize your learning journey.',
|
'We’ll use your name to personalize your learning journey.',
|
||||||
style: TextStyle(
|
style: TextStyle(color: kcMediumGrey),
|
||||||
color: kcMediumGrey,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildFullNameFormField(OnboardingViewModel viewModel) =>
|
Widget _buildFullNameFormField(OnboardingViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: fullNameController,
|
controller: fullNameController,
|
||||||
onTap: viewModel.setFullNameFocus,
|
onTap: viewModel.setFullNameFocus,
|
||||||
decoration: inputDecoration(focus:viewModel.focusFullName),
|
decoration: inputDecoration(focus: viewModel.focusFullName),
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildFullNameValidatorWrapper(OnboardingViewModel viewModel) =>
|
Widget _buildFullNameValidatorWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
|
@ -102,8 +97,8 @@ class FullNameForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
Widget _buildFullNameValidator(OnboardingViewModel viewModel) => Text(
|
Widget _buildFullNameValidator(OnboardingViewModel viewModel) => Text(
|
||||||
viewModel.fullNameValidationMessage!,
|
viewModel.fullNameValidationMessage!,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.red,
|
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
color: Colors.red,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -118,8 +113,13 @@ class FullNameForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
onTap: () => viewModel.next(),
|
|
||||||
foregroundColor: kcWhiteColor,
|
foregroundColor: kcWhiteColor,
|
||||||
backgroundColor: kcPrimaryColor,
|
onTap: viewModel.focusFullName && fullNameController.text.isNotEmpty
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
backgroundColor:
|
||||||
|
viewModel.focusFullName && fullNameController.text.isNotEmpty
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -3,10 +3,10 @@ import 'package:iconsax/iconsax.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/shared/widgets/custom_elevated_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/custom_large_radio_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_large_radio_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/onboarding_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
class LearningGoalForm extends ViewModelWidget<OnboardingViewModel> {
|
class LearningGoalForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
const LearningGoalForm({super.key});
|
const LearningGoalForm({super.key});
|
||||||
|
|
@ -83,8 +83,8 @@ class LearningGoalForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
|
||||||
Widget _buildLearningGoals(OnboardingViewModel viewModel) => ListView.builder(
|
Widget _buildLearningGoals(OnboardingViewModel viewModel) => ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemCount: viewModel.learningGoals.length,
|
itemCount: viewModel.learningGoals.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) => _buildLearningGoal(
|
itemBuilder: (context, index) => _buildLearningGoal(
|
||||||
title: viewModel.learningGoals[index]['title'],
|
title: viewModel.learningGoals[index]['title'],
|
||||||
icon: getIcon(viewModel.learningGoals[index]['icon']),
|
icon: getIcon(viewModel.learningGoals[index]['icon']),
|
||||||
|
|
@ -120,8 +120,12 @@ class LearningGoalForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
onTap: () => viewModel.next(),
|
|
||||||
foregroundColor: kcWhiteColor,
|
foregroundColor: kcWhiteColor,
|
||||||
backgroundColor: kcPrimaryColor,
|
onTap: viewModel.selectedLearningGoal != null
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
backgroundColor: viewModel.selectedLearningGoal != null
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/shared/widgets/custom_elevated_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.form.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.form.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/custom_small_radio_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/onboarding_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
class LearningReasonForm extends ViewModelWidget<OnboardingViewModel> {
|
class LearningReasonForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
final TextEditingController learningReasonController;
|
final TextEditingController learningReasonController;
|
||||||
|
|
@ -63,12 +63,12 @@ class LearningReasonForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
_buildSubTitle(),
|
_buildSubTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildReasons(viewModel),
|
_buildReasons(viewModel),
|
||||||
if (viewModel.showTextBox) _buildReasonFormField(viewModel),
|
if (viewModel.showReasonTextBox) _buildReasonFormField(viewModel),
|
||||||
if (viewModel.showTextBox &&
|
if (viewModel.showReasonTextBox &&
|
||||||
viewModel.hasLearningReasonValidationMessage &&
|
viewModel.hasLearningReasonValidationMessage &&
|
||||||
viewModel.focusLearningReason)
|
viewModel.focusLearningReason)
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
if (viewModel.showTextBox &&
|
if (viewModel.showReasonTextBox &&
|
||||||
viewModel.hasLearningReasonValidationMessage &&
|
viewModel.hasLearningReasonValidationMessage &&
|
||||||
viewModel.focusLearningReason)
|
viewModel.focusLearningReason)
|
||||||
_buildReasonValidatorWrapper(viewModel),
|
_buildReasonValidatorWrapper(viewModel),
|
||||||
|
|
@ -148,8 +148,19 @@ class LearningReasonForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
onTap: () => viewModel.next(),
|
|
||||||
foregroundColor: kcWhiteColor,
|
foregroundColor: kcWhiteColor,
|
||||||
backgroundColor: kcPrimaryColor,
|
onTap: viewModel.selectedLearningReason != null
|
||||||
);
|
? viewModel.selectedLearningReason?.toLowerCase() == 'other'
|
||||||
|
? viewModel.focusLearningReason
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null
|
||||||
|
: () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
backgroundColor: viewModel.selectedLearningReason != null
|
||||||
|
? viewModel.selectedLearningReason?.toLowerCase() == 'other'
|
||||||
|
? viewModel.focusLearningReason && learningReasonController.text.isNotEmpty
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2)
|
||||||
|
: kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2));
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/shared/widgets/custom_elevated_button.dart';
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/onboarding_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
import '../onboarding_view.form.dart';
|
import '../../onboarding_view.form.dart';
|
||||||
|
|
||||||
class OccupationForm extends ViewModelWidget<OnboardingViewModel> {
|
class OccupationForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
final TextEditingController occupationController;
|
final TextEditingController occupationController;
|
||||||
|
|
@ -81,9 +81,7 @@ class OccupationForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
|
||||||
Widget _buildSubTitle() => const Text(
|
Widget _buildSubTitle() => const Text(
|
||||||
'We’ll personalize your learning experience based on your occupation.',
|
'We’ll personalize your learning experience based on your occupation.',
|
||||||
style: TextStyle(
|
style: TextStyle(color: kcMediumGrey),
|
||||||
color: kcMediumGrey,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildOccupationFormField(OnboardingViewModel viewModel) =>
|
Widget _buildOccupationFormField(OnboardingViewModel viewModel) =>
|
||||||
|
|
@ -101,8 +99,8 @@ class OccupationForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
Widget _buildOccupationValidator(OnboardingViewModel viewModel) => Text(
|
Widget _buildOccupationValidator(OnboardingViewModel viewModel) => Text(
|
||||||
viewModel.occupationValidationMessage!,
|
viewModel.occupationValidationMessage!,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.red,
|
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
color: Colors.red,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -117,8 +115,13 @@ class OccupationForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
onTap: () => viewModel.next(),
|
|
||||||
foregroundColor: kcWhiteColor,
|
foregroundColor: kcWhiteColor,
|
||||||
backgroundColor: kcPrimaryColor,
|
onTap: viewModel.focusOccupation && occupationController.text.isNotEmpty
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
backgroundColor:
|
||||||
|
viewModel.focusOccupation && occupationController.text.isNotEmpty
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.form.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class TopicForm extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
final TextEditingController topicController;
|
||||||
|
|
||||||
|
const TopicForm({super.key, required this.topicController});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTopics(viewModel),
|
||||||
|
if (viewModel.showTopicTextBox) _buildTopicFormField(viewModel),
|
||||||
|
if (viewModel.showTopicTextBox &&
|
||||||
|
viewModel.hasTopicValidationMessage &&
|
||||||
|
viewModel.focusTopic)
|
||||||
|
verticalSpaceTiny,
|
||||||
|
if (viewModel.showTopicTextBox &&
|
||||||
|
viewModel.hasTopicValidationMessage &&
|
||||||
|
viewModel.focusTopic)
|
||||||
|
_buildTopicWrapper(viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar();
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Which topics interest you most?',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'Your favorite topics help us create fun, relatable lessons.',
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTopics(OnboardingViewModel viewModel) => ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
itemCount: viewModel.topics.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) => _buildTopic(
|
||||||
|
title: viewModel.topics[index],
|
||||||
|
selected: viewModel.isSelectedTopic(viewModel.topics[index]),
|
||||||
|
onTap: () => viewModel.setSelectedTopic(viewModel.topics[index]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTopic(
|
||||||
|
{required String title,
|
||||||
|
required bool selected,
|
||||||
|
required GestureTapCallback onTap}) =>
|
||||||
|
CustomSmallRadioButton(
|
||||||
|
title: title,
|
||||||
|
onTap: onTap,
|
||||||
|
selected: selected,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTopicFormField(OnboardingViewModel viewModel) => TextFormField(
|
||||||
|
maxLines: 3,
|
||||||
|
controller: topicController,
|
||||||
|
onTap: viewModel.setTopicsFocus,
|
||||||
|
decoration: inputDecoration(focus: true, hint: 'Write you interest…'),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTopicWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
viewModel.hasTopicValidationMessage
|
||||||
|
? _buildTopicValidator(viewModel)
|
||||||
|
: Container();
|
||||||
|
|
||||||
|
Widget _buildTopicValidator(OnboardingViewModel viewModel) => Text(
|
||||||
|
viewModel.topicValidationMessage!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.red,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
onTap: viewModel.selectedTopic != null
|
||||||
|
? viewModel.selectedTopic?.toLowerCase() == 'other'
|
||||||
|
? viewModel.focusTopic
|
||||||
|
? () => viewModel.next()
|
||||||
|
: null
|
||||||
|
: () => viewModel.next()
|
||||||
|
: null,
|
||||||
|
backgroundColor: viewModel.selectedTopic != null
|
||||||
|
? viewModel.selectedTopic?.toLowerCase() == 'other'
|
||||||
|
? viewModel.focusTopic && topicController.text.isNotEmpty
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2)
|
||||||
|
: kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.2));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../../../widgets/custom_small_radio_button.dart';
|
||||||
|
import '../../../widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class LanguageSelector extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const LanguageSelector({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildAppBar(), _buildExpandedBody(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildLanguages(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar() => const OnboardingAppBar(language: true);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Choose your language',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
color: kcDarkGreyColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubTitle() => const Text(
|
||||||
|
'You can switch languages anytime in Settings',
|
||||||
|
style: TextStyle(color: kcMediumGrey),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildLanguages(OnboardingViewModel viewModel) => ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: viewModel.languages.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) => _buildLanguage(
|
||||||
|
title: viewModel.languages[index]['language'],
|
||||||
|
selected: viewModel
|
||||||
|
.isSelectedLanguage(viewModel.languages[index]['language']),
|
||||||
|
onTap: () =>
|
||||||
|
viewModel.setSelectedLanguage(viewModel.languages[index]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildLanguage(
|
||||||
|
{required String title,
|
||||||
|
required bool selected,
|
||||||
|
required GestureTapCallback onTap}) =>
|
||||||
|
CustomSmallRadioButton(
|
||||||
|
title: title,
|
||||||
|
onTap: onTap,
|
||||||
|
selected: selected,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhiteColor,
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
onTap: () => viewModel.pop(language: true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class FirstWelcome extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const FirstWelcome({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Stack(
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) => [
|
||||||
|
_buildBackground(),
|
||||||
|
_buildColumnWrapper(),
|
||||||
|
_buildContinueButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildBackground() => Image.asset(
|
||||||
|
'assets/images/onboarding_1.png',
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildColumnWrapper() => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildColumn(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildColumn() => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren() => [
|
||||||
|
verticalSpaceMassive,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset('assets/icons/logo.svg');
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Small daily practice. Big lifelong results.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 30,
|
||||||
|
color: kcWhiteColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: _buildButtonContainer(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildButtonContainer(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50, right: 50, left: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
icon: true,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Start Learning',
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
backgroundColor: kcWhiteColor,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class SecondWelcome extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const SecondWelcome({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Stack(
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) => [
|
||||||
|
_buildBackground(),
|
||||||
|
_buildColumnWrapper(),
|
||||||
|
_buildContinueButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildBackground() => Image.asset(
|
||||||
|
'assets/images/onboarding_2.png',
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildColumnWrapper() => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildColumn(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildColumn() => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren() => [
|
||||||
|
verticalSpaceMassive,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
|
'assets/icons/logo.svg',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Start speaking, Confidence will follow.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 30,
|
||||||
|
color: kcWhiteColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: _buildButtonContainer(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildButtonContainer(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50, right: 50, left: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
icon: true,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Start Learning',
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
backgroundColor: kcWhiteColor,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/onboarding_app_bar.dart';
|
||||||
|
|
||||||
|
class ThirdWelcome extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
const ThirdWelcome({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(OnboardingViewModel viewModel) => Stack(
|
||||||
|
children: _buildScaffoldChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) => [
|
||||||
|
_buildBackground(),
|
||||||
|
_buildColumnWrapper(),
|
||||||
|
_buildContinueButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildBackground() => Image.asset(
|
||||||
|
'assets/images/onboarding_3.png',
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildColumnWrapper() => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildColumn(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildColumn() => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren() => [
|
||||||
|
verticalSpaceMassive,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset('assets/icons/logo.svg');
|
||||||
|
|
||||||
|
Widget _buildTitle() => const Text(
|
||||||
|
'Every conversation brings you closer to the life you want.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 30,
|
||||||
|
color: kcWhiteColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: _buildButtonContainer(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildButtonContainer(OnboardingViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50, right: 50, left: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
icon: true,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Start Learning',
|
||||||
|
onTap: () => viewModel.next(),
|
||||||
|
backgroundColor: kcWhiteColor,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
|
||||||
|
import '../../common/app_colors.dart';
|
||||||
import 'startup_viewmodel.dart';
|
import 'startup_viewmodel.dart';
|
||||||
|
|
||||||
class StartupView extends StackedView<StartupViewModel> {
|
class StartupView extends StackedView<StartupViewModel> {
|
||||||
|
|
@ -13,36 +15,79 @@ class StartupView extends StackedView<StartupViewModel> {
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
StartupViewModel viewModel,
|
StartupViewModel viewModel,
|
||||||
Widget? child,
|
Widget? child,
|
||||||
) {
|
) =>
|
||||||
return const Scaffold(
|
_buildScaffoldWrapper();
|
||||||
body: Center(
|
|
||||||
child: Column(
|
Widget _buildScaffoldWrapper() => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold() => Stack(
|
||||||
|
children: _buildScaffoldChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren() => [
|
||||||
|
_buildBackground(),
|
||||||
|
_buildColumn(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildBackground() => Image.asset(
|
||||||
|
'assets/images/onboarding_1.png',
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildColumn() => Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren() =>
|
||||||
|
[_buildIconWrapper(), _buildLoadingTextContainer()];
|
||||||
|
|
||||||
|
Widget _buildLoadingTextContainer() => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildLoadingTextWrapper(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildLoadingTextWrapper() => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Text(
|
children: _buildLoadingTextChildren(),
|
||||||
'STACKED',
|
);
|
||||||
style: TextStyle(fontSize: 40, fontWeight: FontWeight.w900),
|
|
||||||
),
|
List<Widget> _buildLoadingTextChildren() => [
|
||||||
Row(
|
_buildLoadingText(),
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text('Loading ...', style: TextStyle(fontSize: 16)),
|
|
||||||
horizontalSpaceSmall,
|
horizontalSpaceSmall,
|
||||||
SizedBox(
|
_buildIndicatorWrapper(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildLoadingText() => const Text('Loading ...',
|
||||||
|
style: TextStyle(color: kcWhiteColor, fontSize: 16));
|
||||||
|
|
||||||
|
Widget _buildIndicatorWrapper() => SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child: CircularProgressIndicator(
|
child: _buildIndicator(),
|
||||||
color: Colors.black,
|
);
|
||||||
strokeWidth: 6,
|
|
||||||
),
|
Widget _buildIndicator() => const CircularProgressIndicator(
|
||||||
),
|
strokeWidth: 6,
|
||||||
],
|
color: kcWhiteColor,
|
||||||
),
|
);
|
||||||
],
|
|
||||||
),
|
Widget _buildIconWrapper() => Padding(
|
||||||
),
|
padding: const EdgeInsets.only(top: 100),
|
||||||
|
child: _buildIcon(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
|
'assets/icons/logo.svg',
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
StartupViewModel viewModelBuilder(BuildContext context) => StartupViewModel();
|
StartupViewModel viewModelBuilder(BuildContext context) => StartupViewModel();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/app/app.locator.dart';
|
|
||||||
import 'package:yimaru_app/app/app.router.dart';
|
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
|
||||||
|
import '../../../app/app.locator.dart';
|
||||||
|
import '../../../app/app.router.dart';
|
||||||
|
|
||||||
class StartupViewModel extends BaseViewModel {
|
class StartupViewModel extends BaseViewModel {
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class CustomElevatedButton extends StatelessWidget {
|
class CustomElevatedButton extends StatelessWidget {
|
||||||
|
final bool icon;
|
||||||
final String text;
|
final String text;
|
||||||
final double width;
|
final double width;
|
||||||
final double height;
|
final double height;
|
||||||
|
final Color? borderColor;
|
||||||
final double borderRadius;
|
final double borderRadius;
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
final Color foregroundColor;
|
final Color foregroundColor;
|
||||||
|
|
@ -12,6 +14,8 @@ class CustomElevatedButton extends StatelessWidget {
|
||||||
const CustomElevatedButton({
|
const CustomElevatedButton({
|
||||||
super.key,
|
super.key,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
|
this.borderColor,
|
||||||
|
this.icon = false,
|
||||||
required this.text,
|
required this.text,
|
||||||
required this.height,
|
required this.height,
|
||||||
this.borderRadius = 0,
|
this.borderRadius = 0,
|
||||||
|
|
@ -29,12 +33,27 @@ class CustomElevatedButton extends StatelessWidget {
|
||||||
Widget _buildButton() => OutlinedButton(
|
Widget _buildButton() => OutlinedButton(
|
||||||
onPressed: onTap,
|
onPressed: onTap,
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
side: BorderSide.none,
|
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
|
side: borderColor == null
|
||||||
|
? BorderSide.none
|
||||||
|
: BorderSide(color: borderColor!),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(borderRadius)),
|
borderRadius: BorderRadius.circular(borderRadius)),
|
||||||
),
|
),
|
||||||
child: _buildText(),
|
child: _buildRow(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildRow() => Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: _buildRowChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildRowChildren() =>
|
||||||
|
[_buildText(), const SizedBox(width: 5), if (icon) _buildIcon()];
|
||||||
|
|
||||||
|
Widget _buildIcon() => Icon(
|
||||||
|
Icons.arrow_forward,
|
||||||
|
color: foregroundColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildText() => Text(
|
Widget _buildText() => Text(
|
||||||
|
|
@ -61,8 +61,8 @@ class CustomLargeRadioButton extends StatelessWidget {
|
||||||
|
|
||||||
Widget _buildLeadingIcon() => Icon(
|
Widget _buildLeadingIcon() => Icon(
|
||||||
icon,
|
icon,
|
||||||
color: kcPrimaryColor,
|
|
||||||
size: 25,
|
size: 25,
|
||||||
|
color: kcPrimaryColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
|
|
@ -76,9 +76,7 @@ class CustomLargeRadioButton extends StatelessWidget {
|
||||||
|
|
||||||
Widget _buildSubTitle() => Text(
|
Widget _buildSubTitle() => Text(
|
||||||
subtitle,
|
subtitle,
|
||||||
style: const TextStyle(
|
style: const TextStyle(color: kcMediumGrey),
|
||||||
color: kcMediumGrey,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSelectedCheckBox() => Checkbox(
|
Widget _buildSelectedCheckBox() => Checkbox(
|
||||||
|
|
@ -3,10 +3,16 @@ import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
|
||||||
class LanguageButton extends StatelessWidget {
|
class LanguageButton extends StatelessWidget {
|
||||||
final String language;
|
final String language;
|
||||||
const LanguageButton({super.key, required this.language});
|
final GestureTapCallback? onTap;
|
||||||
|
|
||||||
|
const LanguageButton({super.key, this.onTap, required this.language});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildContainer();
|
Widget build(BuildContext context) => _buildContainerWrapper();
|
||||||
|
|
||||||
|
Widget _buildContainerWrapper() =>
|
||||||
|
GestureDetector(onTap: onTap, child: _buildContainer());
|
||||||
|
|
||||||
Widget _buildContainer() => Container(
|
Widget _buildContainer() => Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
|
|
@ -2,14 +2,16 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/onboarding/widgets/language_button.dart';
|
import 'package:yimaru_app/ui/widgets/language_button.dart';
|
||||||
|
|
||||||
class OnboardingAppBar extends ViewModelWidget<OnboardingViewModel> {
|
class OnboardingAppBar extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
final bool language;
|
||||||
final bool showBackButton;
|
final bool showBackButton;
|
||||||
final bool showLanguageSelection;
|
final bool showLanguageSelection;
|
||||||
|
|
||||||
const OnboardingAppBar(
|
const OnboardingAppBar(
|
||||||
{super.key,
|
{super.key,
|
||||||
|
this.language = false,
|
||||||
this.showBackButton = true,
|
this.showBackButton = true,
|
||||||
this.showLanguageSelection = true});
|
this.showLanguageSelection = true});
|
||||||
|
|
||||||
|
|
@ -38,7 +40,7 @@ class OnboardingAppBar extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
|
||||||
List<Widget> _buildAppBarItemChildren(OnboardingViewModel viewModel) => [
|
List<Widget> _buildAppBarItemChildren(OnboardingViewModel viewModel) => [
|
||||||
if (showBackButton) _buildBackButtonWrapper(viewModel),
|
if (showBackButton) _buildBackButtonWrapper(viewModel),
|
||||||
_buildRightButton()
|
_buildRightButton(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildBackButtonWrapper(OnboardingViewModel viewModel) => Align(
|
Widget _buildBackButtonWrapper(OnboardingViewModel viewModel) => Align(
|
||||||
|
|
@ -47,18 +49,22 @@ class OnboardingAppBar extends ViewModelWidget<OnboardingViewModel> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBackButton(OnboardingViewModel viewModel) => BackButton(
|
Widget _buildBackButton(OnboardingViewModel viewModel) => BackButton(
|
||||||
onPressed: viewModel.pop,
|
onPressed: ()=> viewModel.pop(language: language),
|
||||||
style: const ButtonStyle(
|
style: const ButtonStyle(
|
||||||
foregroundColor: WidgetStatePropertyAll(kcWhiteColor)),
|
foregroundColor: WidgetStatePropertyAll(kcWhiteColor)),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildRightButton() => Align(
|
Widget _buildRightButton(OnboardingViewModel viewModel) => Align(
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
child: showLanguageSelection
|
child: showLanguageSelection
|
||||||
? _buildLanguageSelector()
|
? _buildLanguageSelector(viewModel)
|
||||||
: _buildCloseButton());
|
: _buildCloseButton());
|
||||||
|
|
||||||
Widget _buildLanguageSelector() => const LanguageButton(language: 'EN');
|
Widget _buildLanguageSelector(OnboardingViewModel viewModel) =>
|
||||||
|
LanguageButton(
|
||||||
|
language: 'EN',
|
||||||
|
onTap: () => viewModel.next(page: 23),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildCloseButton() => IconButton(
|
Widget _buildCloseButton() => IconButton(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
|
|
@ -214,6 +214,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.3"
|
||||||
|
flutter_svg:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_svg
|
||||||
|
sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -275,6 +283,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.3.2"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -435,6 +451,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -616,6 +648,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.1"
|
||||||
|
vector_graphics:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics
|
||||||
|
sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.19"
|
||||||
|
vector_graphics_codec:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics_codec
|
||||||
|
sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.13"
|
||||||
|
vector_graphics_compiler:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics_compiler
|
||||||
|
sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.19"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -672,6 +728,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.6.1"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -682,4 +746,4 @@ packages:
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.0 <4.0.0"
|
dart: ">=3.8.0 <4.0.0"
|
||||||
flutter: ">=3.18.0-18.0.pre.54"
|
flutter: ">=3.32.0"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ dependencies:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
stacked: ^3.4.0
|
stacked: ^3.4.0
|
||||||
iconsax: ^0.0.8
|
iconsax: ^0.0.8
|
||||||
|
flutter_svg: ^2.2.3
|
||||||
dropdown_search: ^6.0.2
|
dropdown_search: ^6.0.2
|
||||||
stacked_services: ^1.1.0
|
stacked_services: ^1.1.0
|
||||||
|
|
||||||
|
|
@ -28,3 +29,13 @@ dev_dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
|
assets:
|
||||||
|
- assets/icons/
|
||||||
|
- assets/images/
|
||||||
|
fonts:
|
||||||
|
- family: Aeonik
|
||||||
|
fonts:
|
||||||
|
- asset: assets/fonts/Aeonik-Regular.ttf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('LanguageViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SettingsViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('StartupViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||