diff --git a/packages/smooth_app/assets/fonts/SmoothIcons.ttf b/packages/smooth_app/assets/fonts/SmoothIcons.ttf
index 5b3749be42c..b2259c6ae55 100644
Binary files a/packages/smooth_app/assets/fonts/SmoothIcons.ttf and b/packages/smooth_app/assets/fonts/SmoothIcons.ttf differ
diff --git a/packages/smooth_app/assets/fonts/icons/config.json b/packages/smooth_app/assets/fonts/icons/config.json
index 52d201bba87..29758767340 100644
--- a/packages/smooth_app/assets/fonts/icons/config.json
+++ b/packages/smooth_app/assets/fonts/icons/config.json
@@ -1551,6 +1551,132 @@
"search": [
"compare_disabled"
]
+ },
+ {
+ "uid": "a4ec317df24bc98a3ee67a3f58486388",
+ "css": "lists",
+ "code": 59488,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M83.8 142.9C44.4 142.9 12.4 174.8 12.4 214.3 12.4 253.7 44.4 285.7 83.8 285.7 123.3 285.7 155.2 253.7 155.2 214.3 155.2 174.8 123.3 142.9 83.8 142.9ZM321.9 166.7C304.7 166.4 288.8 175.4 280.1 190.3 271.4 205.1 271.4 223.5 280.1 238.3 288.8 253.1 304.7 262.1 321.9 261.9L941 261.9C958.1 262.1 974.1 253.1 982.8 238.3 991.4 223.5 991.4 205.1 982.8 190.3 974.1 175.4 958.1 166.4 941 166.7L321.9 166.7ZM83.8 428.6C44.4 428.6 12.4 460.6 12.4 500 12.4 539.4 44.4 571.4 83.8 571.4 123.3 571.4 155.2 539.4 155.2 500 155.2 460.6 123.3 428.6 83.8 428.6ZM321.9 452.4C304.7 452.1 288.8 461.2 280.1 476 271.4 490.8 271.4 509.2 280.1 524 288.8 538.8 304.7 547.9 321.9 547.6L941 547.6C958.1 547.9 974.1 538.8 982.8 524 991.4 509.2 991.4 490.8 982.8 476 974.1 461.2 958.1 452.1 941 452.4L321.9 452.4ZM83.8 714.3C44.4 714.3 12.4 746.3 12.4 785.7 12.4 825.2 44.4 857.1 83.8 857.1 123.3 857.1 155.2 825.2 155.2 785.7 155.2 746.3 123.3 714.3 83.8 714.3ZM321.9 738.1C304.7 737.9 288.8 746.9 280.1 761.7 271.4 776.5 271.4 794.9 280.1 809.7 288.8 824.6 304.7 833.6 321.9 833.3L941 833.3C958.1 833.6 974.1 824.6 982.8 809.7 991.4 794.9 991.4 776.5 982.8 761.7 974.1 746.9 958.1 737.9 941 738.1L321.9 738.1Z",
+ "width": 1000
+ },
+ "search": [
+ "lists"
+ ]
+ },
+ {
+ "uid": "183ded7c767a0c3e2bcab8705bb32b18",
+ "css": "search_alt",
+ "code": 59503,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M350 0C157.3 0 0 157.3 0 350 0 542.7 157.3 700 350 700 437.4 700 517.3 667.4 578.7 614.1L600 635.4 600 700 879.3 979.3C906.9 1006.9 951.7 1006.9 979.3 979.3 1006.9 951.7 1006.9 906.9 979.3 879.3L700 600 635.4 600 614.1 578.7C667.4 517.3 700 437.4 700 350 700 157.3 542.7 0 350 0ZM350 100C488.7 100 600 211.3 600 350 600 488.7 488.7 600 350 600 211.3 600 100 488.7 100 350 100 211.3 211.3 100 350 100Z",
+ "width": 1000
+ },
+ "search": [
+ "search_alt"
+ ]
+ },
+ {
+ "uid": "82a6c5d6edacf462105d96c923153c13",
+ "css": "profile",
+ "code": 59504,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M500 0C223.9 0 0 223.9 0 500 0 776.1 223.9 1000 500 1000 776.1 1000 1000 776.1 1000 500 1000 223.9 776.1 0 500 0ZM500 137.5C589.8 137.5 662.5 210.3 662.5 300 662.5 389.8 589.8 462.5 500 462.5 410.3 462.5 337.5 389.8 337.5 300 337.5 210.3 410.3 137.5 500 137.5ZM500 900C378.1 900 268.9 845.4 195.6 759.4 176.6 737 180.8 703.5 204.3 685.9 280.1 628.8 418.3 600 500 600 581.7 600 719.9 628.8 795.8 685.9 819.3 703.5 823.5 737.1 804.4 759.4 731.1 845.4 621.9 900 500 900Z",
+ "width": 1000
+ },
+ "search": [
+ "profile"
+ ]
+ },
+ {
+ "uid": "79f94627c4319d51557321b11509a35b",
+ "css": "photo_select",
+ "code": 59505,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M792.3 479.4C652.7 479.4 539.1 365.8 539.1 226.2 539.1 193.8 544.8 162.9 556.2 134.5L110.7 134.5C101 134.5 91.2 135.4 81.5 137 34.4 144.3 0.4 184 0.4 230.3 0.4 450.2-0.5 669.3 0.4 888.3 0.4 943.5 48.2 992.2 103.4 992.2L754.1 992.2C773.6 992.2 790.7 985.7 806.1 975.1 834.5 953.2 847.5 922.4 847.5 886.7 848.3 778 848.3 668.4 848.3 558.9L848.3 472.9C830.4 477 811.8 479.4 792.3 479.4ZM252.7 262.7C314.4 261.9 364.7 312.2 364.7 374.7 364.7 436.4 316 485.9 253.5 486.7 191.8 486.7 141.5 436.4 141.5 374.7 142.4 313.1 191.8 262.7 252.7 262.7ZM746 849.4C743.6 852.6 737.9 855.1 729.8 855.1 729 855.9 728.2 855.9 725.7 855.9L299 855.9C266.5 855.9 234 855.1 202.4 855.1 187.8 855.1 172.4 854.2 157.8 854.2 144 854.2 130.2 856.7 116.4 855.9 115.6 855.9 115.6 855.9 114.8 855.1 114 855.1 112.3 855.1 110.7 853.4 101.8 847.8 120.4 834 123.7 829.9 133.4 817.7 241.3 666 243 664.4 257.6 642.5 277 630.3 298.1 630.3 329 630.3 344.4 662.8 368.7 676.6 400.4 695.2 420.7 670.1 436.9 645.7 447.4 629.5 458 612.5 468.5 595.4 475 585.7 481.5 575.1 487.2 564.6 494.5 552.4 502.6 541.1 509.9 528.9L525.3 504.5C527 502.9 527.8 501.3 528.6 499.7 534.3 491.6 540.8 486.7 545.6 486.7 550.5 486.7 556.2 491.6 561 500.5 613.8 593.8 666.5 689.5 718.4 782.8L743.6 828.3C746 831.5 746.8 835.6 747.7 838 748.5 841.3 748.5 846.1 746 849.4ZM592.6 222.2C592.6 334.7 683.8 425.9 796.3 425.9 908.8 425.9 1000 334.7 1000 222.2 1000 109.7 908.8 18.5 796.3 18.5 683.8 18.5 592.6 109.7 592.6 222.2ZM808.3 125.3L876.2 193.2C879.5 196.6 881.2 200.9 881.2 205.2 881.2 209.6 879.5 213.9 876.2 217.2 869.6 223.9 858.8 223.9 852.2 217.2L813.3 178.3 813.3 307.1C813.3 316.5 805.7 324.1 796.3 324.1 786.9 324.1 779.3 316.5 779.3 307.1L779.3 178.3 740.4 217.2C733.8 223.9 723 223.9 716.4 217.2 709.8 210.6 709.8 199.9 716.4 193.2L784.3 125.3C790.9 118.7 801.7 118.7 808.3 125.3Z",
+ "width": 1000
+ },
+ "search": [
+ "photo_select"
+ ]
+ },
+ {
+ "uid": "d005ece76711ed19d5945721ee4d68bb",
+ "css": "ingredients_alt",
+ "code": 59616,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M680 475L0 475 0 625C0 820 155 975 350 975L650 975C845 975 1000 820 1000 625L1000 475 680 475ZM941.3 126.2L960.2 138C983.6 152.6 990.7 183.5 976.1 206.9L839.7 425 695.6 425 872.4 142.1C887.1 118.7 917.9 111.6 941.3 126.2Z",
+ "width": 1000
+ },
+ "search": [
+ "ingredients_alt"
+ ]
+ },
+ {
+ "uid": "fde39b122920058c09766489a6eb2df1",
+ "css": "milk_filled",
+ "code": 59617,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M691.5 0C711.7 0 728 16.4 728 36.6L728 76.7 835.8 292.3 840.1 301.8C846.6 317.8 850 334.9 850 352.3L850 914.6C850 961.3 811.3 1000 764.6 1000L203.7 1000C156.9 1000 118.3 961.3 118.3 914.6L118.3 352.3C118.3 331.5 123.1 310.9 132.4 292.3L240.2 76.7 240.2 36.6C240.2 16.4 256.6 0 276.8 0ZM691.5 167.2L612.5 325C608.3 333.4 606.1 342.8 606.1 352.3L606.1 926.8 764.6 926.8C771.8 926.8 776.8 921.8 776.8 914.6L776.8 352.3C776.8 342.8 774.6 333.5 770.4 325L691.5 167.2ZM271.1 637C256 637.3 242.7 646.9 237.6 661.1 232.5 675.3 236.6 691.2 247.9 701.1 311.4 759.9 408.9 759.9 472.4 701.1 483.6 691 487.5 675 482.2 660.9 476.8 646.8 463.4 637.4 448.3 637.3 438.8 637.2 429.6 640.8 422.7 647.4 385.2 682.1 335 682.1 297.6 647.4 290.5 640.5 281 636.7 271.1 637ZM276.8 512.2C256.6 512.2 240.2 528.6 240.2 548.8 240.2 569 256.6 585.4 276.8 585.4 297 585.4 313.4 569 313.4 548.8 313.4 528.6 297 512.2 276.8 512.2ZM447.6 512.2C427.3 512.2 411 528.6 411 548.8 411 569 427.3 585.4 447.6 585.4 467.8 585.4 484.1 569 484.1 548.8 484.1 528.6 467.8 512.2 447.6 512.2ZM632.2 122L299.4 122 214 292.7 546.9 292.7 632.2 122Z",
+ "width": 1000
+ },
+ "search": [
+ "milk_filled"
+ ]
+ },
+ {
+ "uid": "edd504c105f6af765a06a48356beac49",
+ "css": "recycling",
+ "code": 59618,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M693.3 0.3L427.7 1.6C436.5 4.1 456.4 7.5 470.7 31.6L602.2 260.7 530.6 302.3 763.6 301 878.1 100.6 806.5 142.2 738.8 23.8C731.7 8.6 720.4-2.1 693.3 0.3ZM365.3 29C330 26.7 289 39.1 258.5 81L257.2 85 172.6 234.6 375.7 351.8 490.2 150 440.8 64.1C431.2 48.2 400.6 31.3 365.3 29ZM895 320.5L692 437.7 807.8 639.4 906.7 639.4C943.5 638.6 1024.5 568.5 982.2 474.1L980.9 468.9 895 320.5ZM11.2 368.7L82.8 410.4 13.8 528.8C5 542.3 1.3 557.1 16.4 579.6L150.5 810C148.1 800.4 142.2 780.5 155.7 756.6L287.2 528.8 358.8 570.5 241.6 368.7 11.2 368.7ZM650.3 597.8L534.5 799.5 649 1000 650.3 916.7 787 916.7C802.9 918.2 817.4 914.1 828.6 889.4L961.4 659C955.1 665.3 941.2 680.3 913.2 681.1L650.3 681.1 650.3 597.8ZM246.8 679.8L197.4 765.7C179.8 798.6 199.6 903.7 302.8 914.1L308 915.4 479.8 915.4 479.8 679.8 246.8 679.8Z",
+ "width": 1000
+ },
+ "search": [
+ "recycling"
+ ]
+ },
+ {
+ "uid": "e66f082674ce91622442c49575bab8d3",
+ "css": "image_check_alt",
+ "code": 59509,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M792.3 479.4C652.7 479.4 539.1 365.8 539.1 226.2 539.1 193.8 544.8 162.9 556.2 134.5L110.7 134.5C101 134.5 91.2 135.4 81.5 137 34.4 144.3 0.4 184 0.4 230.3 0.4 450.2-0.5 669.3 0.4 888.3 0.4 943.5 48.2 992.2 103.4 992.2L754.1 992.2C773.6 992.2 790.7 985.7 806.1 975.1 834.5 953.2 847.5 922.4 847.5 886.7 848.3 778 848.3 668.4 848.3 558.9L848.3 472.9C830.4 477 811.8 479.4 792.3 479.4ZM252.7 262.7C314.4 261.9 364.7 312.2 364.7 374.7 364.7 436.4 316 485.9 253.5 486.7 191.8 486.7 141.5 436.4 141.5 374.7 142.4 313.1 191.8 262.7 252.7 262.7ZM746 849.4C743.6 852.6 737.9 855.1 729.8 855.1 729 855.9 728.2 855.9 725.7 855.9L299 855.9C266.5 855.9 234 855.1 202.4 855.1 187.8 855.1 172.4 854.2 157.8 854.2 144 854.2 130.2 856.7 116.4 855.9 115.6 855.9 115.6 855.9 114.8 855.1 114 855.1 112.3 855.1 110.7 853.4 101.8 847.8 120.4 834 123.7 829.9 133.4 817.7 241.3 666 243 664.4 257.6 642.5 277 630.3 298.1 630.3 329 630.3 344.4 662.8 368.7 676.6 400.4 695.2 420.7 670.1 436.9 645.7 447.4 629.5 458 612.5 468.5 595.4 475 585.7 481.5 575.1 487.2 564.6 494.5 552.4 502.6 541.1 509.9 528.9L525.3 504.5C527 502.9 527.8 501.3 528.6 499.7 534.3 491.6 540.8 486.7 545.6 486.7 550.5 486.7 556.2 491.6 561 500.5 613.8 593.8 666.5 689.5 718.4 782.8L743.6 828.3C746 831.5 746.8 835.6 747.7 838 748.5 841.3 748.5 846.1 746 849.4ZM796.3 18.5C683.8 18.5 592.6 109.7 592.6 222.2 592.6 334.7 683.8 425.9 796.3 425.9 908.8 425.9 1000 334.7 1000 222.2 1000 109.7 908.8 18.5 796.3 18.5ZM906.6 170L777.2 299.3C774 302.6 769.6 304.4 765.1 304.4 760.6 304.4 756.2 302.6 753 299.3L693.9 240.2C687.2 233.6 687.2 222.7 693.9 216 700.6 209.4 711.4 209.4 718.1 216L765.1 263 882.4 145.8C889.1 139.1 899.9 139.1 906.6 145.8 913.3 152.5 913.3 163.3 906.6 170Z",
+ "width": 1000
+ },
+ "search": [
+ "image_check_alt"
+ ]
+ },
+ {
+ "uid": "150e5961b31e3232a1f05d78cf4f7b8a",
+ "css": "image_error",
+ "code": 59510,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M792.3 479.4C652.7 479.4 539.1 365.8 539.1 226.2 539.1 193.8 544.8 162.9 556.2 134.5L110.7 134.5C101 134.5 91.2 135.4 81.5 137 34.4 144.3 0.4 184 0.4 230.3 0.4 450.2-0.5 669.3 0.4 888.3 0.4 943.5 48.2 992.2 103.4 992.2L754.1 992.2C773.6 992.2 790.7 985.7 806.1 975.1 834.5 953.2 847.5 922.4 847.5 886.7 848.3 778 848.3 668.4 848.3 558.9L848.3 472.9C830.4 477 811.8 479.4 792.3 479.4ZM252.7 262.7C314.4 261.9 364.7 312.2 364.7 374.7 364.7 436.4 316 485.9 253.5 486.7 191.8 486.7 141.5 436.4 141.5 374.7 142.4 313.1 191.8 262.7 252.7 262.7ZM746 849.4C743.6 852.6 737.9 855.1 729.8 855.1 729 855.9 728.2 855.9 725.7 855.9L299 855.9C266.5 855.9 234 855.1 202.4 855.1 187.8 855.1 172.4 854.2 157.8 854.2 144 854.2 130.2 856.7 116.4 855.9 115.6 855.9 115.6 855.9 114.8 855.1 114 855.1 112.3 855.1 110.7 853.4 101.8 847.8 120.4 834 123.7 829.9 133.4 817.7 241.3 666 243 664.4 257.6 642.5 277 630.3 298.1 630.3 329 630.3 344.4 662.8 368.7 676.6 400.4 695.2 420.7 670.1 436.9 645.7 447.4 629.5 458 612.5 468.5 595.4 475 585.7 481.5 575.1 487.2 564.6 494.5 552.4 502.6 541.1 509.9 528.9L525.3 504.5C527 502.9 527.8 501.3 528.6 499.7 534.3 491.6 540.8 486.7 545.6 486.7 550.5 486.7 556.2 491.6 561 500.5 613.8 593.8 666.5 689.5 718.4 782.8L743.6 828.3C746 831.5 746.8 835.6 747.7 838 748.5 841.3 748.5 846.1 746 849.4ZM796.3 18.5C908.8 18.5 1000 109.7 1000 222.2 1000 334.7 908.8 425.9 796.3 425.9 683.8 425.9 592.6 334.7 592.6 222.2 592.6 109.7 683.8 18.5 796.3 18.5ZM735.3 135.1C728.1 127.8 716.4 127.8 709.1 135.1 701.9 142.3 701.9 154 709.1 161.2 709.6 161.7 711.4 163.5 714.2 166.3L770.1 222.2C770.1 222.2 770 222.4 769.7 222.7L766.2 226.1C758 234.3 738.7 253.6 724.8 267.5L709.1 283.2C701.9 290.4 701.9 302.2 709.1 309.4 716.4 316.6 728.1 316.6 735.3 309.4 736.4 308.4 744.9 299.8 755.3 289.4L782.5 262.2C786.5 258.2 790 254.8 792.4 252.3L795.8 248.9 796.3 248.4 837.3 289.4C841.8 293.9 845.9 298 849.2 301.3L857.3 309.4C864.5 316.6 876.2 316.6 883.5 309.4 890.7 302.2 890.7 290.5 883.5 283.2L822.5 222.2C822.5 222.2 822.7 222.1 823 221.8L826.4 218.3C828.8 215.9 832.3 212.4 836.3 208.4L875.4 169.3C879.8 164.9 882.9 161.8 883.5 161.2 890.7 154 890.7 142.3 883.5 135.1 876.3 127.8 864.5 127.8 857.3 135.1L852.3 140.1C848.6 143.8 843.2 149.1 837.3 155.1L810.1 182.2C806.1 186.2 802.7 189.7 800.2 192.1L796.8 195.6 796.3 196 786.3 186C777.9 177.7 765.7 165.4 755.3 155L747 146.7C740.7 140.4 736.1 135.8 735.3 135.1Z",
+ "width": 1000
+ },
+ "search": [
+ "image_error"
+ ]
}
]
}
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/icons.sketch b/packages/smooth_app/assets/fonts/icons/icons.sketch
index 2810780437f..339876b2d19 100644
Binary files a/packages/smooth_app/assets/fonts/icons/icons.sketch and b/packages/smooth_app/assets/fonts/icons/icons.sketch differ
diff --git a/packages/smooth_app/assets/fonts/icons/image_check_alt.svg b/packages/smooth_app/assets/fonts/icons/image_check_alt.svg
new file mode 100644
index 00000000000..41fe4614b89
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/image_check_alt.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/image_error.svg b/packages/smooth_app/assets/fonts/icons/image_error.svg
new file mode 100644
index 00000000000..b818b39b0ee
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/image_error.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/ingredients_alt.svg b/packages/smooth_app/assets/fonts/icons/ingredients_alt.svg
new file mode 100644
index 00000000000..0149f620a9e
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/ingredients_alt.svg
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/lists.svg b/packages/smooth_app/assets/fonts/icons/lists.svg
new file mode 100644
index 00000000000..ce66cc5edc9
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/lists.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/milk_filled.svg b/packages/smooth_app/assets/fonts/icons/milk_filled.svg
new file mode 100644
index 00000000000..1c7a89523db
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/milk_filled.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/photo_select.svg b/packages/smooth_app/assets/fonts/icons/photo_select.svg
new file mode 100644
index 00000000000..98d486c5a79
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/photo_select.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/profile.svg b/packages/smooth_app/assets/fonts/icons/profile.svg
new file mode 100644
index 00000000000..c15b5587b94
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/profile.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/recycling.svg b/packages/smooth_app/assets/fonts/icons/recycling.svg
new file mode 100644
index 00000000000..0983ea0bba7
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/recycling.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/assets/fonts/icons/search_alt.svg b/packages/smooth_app/assets/fonts/icons/search_alt.svg
new file mode 100644
index 00000000000..8f92765c409
--- /dev/null
+++ b/packages/smooth_app/assets/fonts/icons/search_alt.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/packages/smooth_app/ios/Podfile.lock b/packages/smooth_app/ios/Podfile.lock
index 0b1639f34a1..e4b8769923f 100644
--- a/packages/smooth_app/ios/Podfile.lock
+++ b/packages/smooth_app/ios/Podfile.lock
@@ -22,7 +22,7 @@ PODS:
- Mantle
- SDWebImage
- SDWebImageWebPCoder
- - flutter_native_splash (0.0.1):
+ - flutter_native_splash (2.4.3):
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
@@ -270,7 +270,7 @@ SPEC CHECKSUMS:
flutter_email_sender: 10a22605f92809a11ef52b2f412db806c6082d40
flutter_icmp_ping: 2b159955eee0c487c766ad83fec224ae35e7c935
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
- flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
+ flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleMLKit: 2bd0dc6253c4d4f227aad460f69215a504b2980e
diff --git a/packages/smooth_app/lib/cards/product_cards/smooth_product_image.dart b/packages/smooth_app/lib/cards/product_cards/smooth_product_image.dart
index 0321ebbc6af..4636cb32f31 100644
--- a/packages/smooth_app/lib/cards/product_cards/smooth_product_image.dart
+++ b/packages/smooth_app/lib/cards/product_cards/smooth_product_image.dart
@@ -265,6 +265,7 @@ class _ProductPictureWithImageProvider extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
+ final bool lightTheme = context.lightTheme();
final Widget image = Semantics(
label: appLocalizations.product_page_image_front_accessibility_label,
@@ -276,10 +277,10 @@ class _ProductPictureWithImageProvider extends StatelessWidget {
children: [
Positioned.fill(
child: ColoredBox(
- color: Colors.white,
+ color: lightTheme ? Colors.white : Colors.black,
child: ClipRRect(
child: Opacity(
- opacity: context.lightTheme() ? 0.2 : 0.65,
+ opacity: lightTheme ? 0.2 : 0.55,
child: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
child: Image(
@@ -370,13 +371,9 @@ class _ProductPictureWithImageProvider extends StatelessWidget {
if (loadingProgress == null) {
return child;
}
- return Center(
- child: CircularProgressIndicator.adaptive(
- value: loadingProgress.expectedTotalBytes != null
- ? loadingProgress.cumulativeBytesLoaded /
- loadingProgress.expectedTotalBytes!
- : null,
- ),
+
+ return const Center(
+ child: CircularProgressIndicator(),
);
},
errorBuilder: (_, __, ___) {
diff --git a/packages/smooth_app/lib/generic_lib/bottom_sheets/smooth_bottom_sheet.dart b/packages/smooth_app/lib/generic_lib/bottom_sheets/smooth_bottom_sheet.dart
index 7f4e276eaa5..9f37e80b2a1 100644
--- a/packages/smooth_app/lib/generic_lib/bottom_sheets/smooth_bottom_sheet.dart
+++ b/packages/smooth_app/lib/generic_lib/bottom_sheets/smooth_bottom_sheet.dart
@@ -70,6 +70,66 @@ Future showSmoothDraggableModalSheet({
);
}
+/// A modal sheet containing a limited list (no scroll)
+Future showSmoothListOfChoicesModalSheet({
+ required BuildContext context,
+ required String title,
+ required Iterable labels,
+ required Iterable values,
+ List? prefixIcons,
+ Color? prefixIconTint,
+ List? suffixIcons,
+ Color? suffixIconTint,
+ EdgeInsetsGeometry? padding,
+}) {
+ assert(labels.length == values.length);
+ assert(prefixIcons == null || values.length == prefixIcons.length);
+ assert(suffixIcons == null || values.length == suffixIcons.length);
+ assert(prefixIconTint == null || prefixIcons != null);
+ assert(suffixIconTint == null || suffixIcons != null);
+
+ final List items = [];
+ for (int i = 0; i < labels.length; i++) {
+ items.add(
+ ListTile(
+ leading: prefixIcons != null
+ ? IconTheme.merge(
+ data: IconThemeData(color: prefixIconTint),
+ child: prefixIcons[i])
+ : null,
+ title: Text(
+ labels.elementAt(i),
+ style: const TextStyle(fontWeight: FontWeight.w500),
+ ),
+ contentPadding: padding,
+ trailing: suffixIcons != null
+ ? IconTheme.merge(
+ data: IconThemeData(color: suffixIconTint),
+ child: suffixIcons[i])
+ : null,
+ onTap: () {
+ Navigator.of(context).pop(values.elementAt(i));
+ },
+ ),
+ );
+
+ if (i < labels.length - 1) {
+ items.add(const Divider(height: 1.0));
+ }
+ }
+ items.add(SizedBox(height: MediaQuery.of(context).padding.bottom));
+
+ return showSmoothModalSheet(
+ context: context,
+ builder: (BuildContext context) => SmoothModalSheet(
+ title: title,
+ prefixIndicator: true,
+ bodyPadding: EdgeInsets.zero,
+ body: Column(children: items),
+ ),
+ );
+}
+
/// A non scrollable modal sheet
class SmoothModalSheet extends StatelessWidget {
SmoothModalSheet({
@@ -389,17 +449,14 @@ class SmoothModalSheetHeaderPrefixIndicator extends StatelessWidget
@override
Widget build(BuildContext context) {
- final SmoothColorsThemeExtension extension =
- context.extension();
-
- return Padding(
- padding: const EdgeInsetsDirectional.only(end: VERY_SMALL_SPACE),
+ return const Padding(
+ padding: EdgeInsetsDirectional.only(end: VERY_SMALL_SPACE),
child: SizedBox(
width: 10.0,
height: 10.0,
child: DecoratedBox(
decoration: BoxDecoration(
- color: extension.secondaryNormal,
+ color: Colors.white,
shape: BoxShape.circle,
),
),
diff --git a/packages/smooth_app/lib/generic_lib/loading_dialog.dart b/packages/smooth_app/lib/generic_lib/loading_dialog.dart
index fb032a27b5f..324780fd7f0 100644
--- a/packages/smooth_app/lib/generic_lib/loading_dialog.dart
+++ b/packages/smooth_app/lib/generic_lib/loading_dialog.dart
@@ -1,5 +1,6 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
@@ -114,7 +115,10 @@ class LoadingDialog {
}
_popEd = true;
// Here we use the root navigator so that we can pop dialog while using multiple navigators.
- Navigator.of(context, rootNavigator: true).pop(value);
+
+ SchedulerBinding.instance.addPostFrameCallback((_) {
+ Navigator.of(context, rootNavigator: true).pop(value);
+ });
}
/// Displayed dialog during future.
diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb
index 88519e4de80..869a016f1e0 100644
--- a/packages/smooth_app/lib/l10n/app_en.arb
+++ b/packages/smooth_app/lib/l10n/app_en.arb
@@ -784,6 +784,42 @@
}
},
"product_image_outdated": "This image may be outdated",
+ "product_image_action_replace_photo": "Replace photo ({type})",
+ "@product_image_action_replace_photo": {
+ "description": "Action on the photo gallery to replace an existing picture",
+ "placeholders": {
+ "type": {
+ "type": "String",
+ "description": "The type of the photo (eg: ingredients)"
+ }
+ }
+ },
+ "product_image_action_add_photo": "Add a photo ({type})",
+ "@product_image_action_add_photo": {
+ "description": "Action on the photo gallery to add a photo (eg: ingredients)",
+ "placeholders": {
+ "type": {
+ "type": "String",
+ "description": "The type of the photo (eg: ingredients)"
+ }
+ }
+ },
+ "product_image_action_take_new_picture": "Take a new picture",
+ "@product_image_action_take_new_picture": {
+ "description": "Replace the existing picture with a new one"
+ },
+ "product_image_action_take_picture": "Take a picture",
+ "@product_image_action_take_picture": {
+ "description": "Take a picture (eg: for ingredients) when there is no one"
+ },
+ "product_image_action_from_gallery": "Select from your phone's gallery",
+ "@product_image_action_from_gallery": {
+ "description": "Replace the existing picture with one from the device's gallery"
+ },
+ "product_image_action_choose_existing_photo": "Select from the product photos",
+ "@product_image_action_choose_existing_photo": {
+ "description": "Replace the existing picture with one from the product's photos"
+ },
"homepage_main_card_logo_description": "Welcome to Open Food Facts",
"@homepage_main_card_logo_description": {
"description": "Description for accessibility of the Open Food Facts logo on the homepage"
@@ -2172,10 +2208,14 @@
"@edit_photo_select_existing_button_label": {
"description": "Edit 'select existing image' button label"
},
- "edit_photo_select_existing_all_label": "Existing images",
+ "edit_photo_select_existing_all_label": "Existing images for this product",
"@edit_photo_select_existing_all_label": {
"description": "Page title"
},
+ "edit_photo_select_existing_all_subtitle": "Select an image by clicking on it",
+ "@edit_photo_select_existing_all_subtitle": {
+ "description": "Page title"
+ },
"edit_photo_select_existing_download_label": "Retrieving existing images…",
"@edit_photo_select_existing_download_label": {
"description": "Dialog label"
@@ -3107,6 +3147,19 @@
"@error_loading_photo": {
"description": "Error message when loading a photo fails to load"
},
+ "photo_viewer_action_use_picture_as": "Use as…",
+ "@photo_viewer_action_use_picture_as": {
+ "description": "An action to use the current picture to be used as front/ingredients…"
+ },
+ "photo_viewer_use_picture_as_title": "Use this picture as… ({language})",
+ "@photo_viewer_use_picture_as": {
+ "description": "The title of a dialog to use the current picture as front/ingredients…",
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
"photo_viewer_details_button": "Details",
"@photo_viewer_details_button": {
"description": "Button to show details of the photo"
@@ -3287,5 +3340,11 @@
"product_type_subtitle_product": "Smartphones, furniture…",
"@product_type_subtitle_product" : {
"description": "Example of products for other categories"
- }
+ },
+ "photo_field_front": "Product photo",
+ "photo_field_ingredients": "Ingredients photo",
+ "photo_field_nutrition": "Nutrition photo",
+ "photo_field_packaging": "Packaging information photo",
+ "photo_already_exists": "This photo already exists",
+ "photo_missing": "This photo is missing"
}
diff --git a/packages/smooth_app/lib/pages/crop_page.dart b/packages/smooth_app/lib/pages/crop_page.dart
index e7a832c5c71..1e007e26841 100644
--- a/packages/smooth_app/lib/pages/crop_page.dart
+++ b/packages/smooth_app/lib/pages/crop_page.dart
@@ -4,6 +4,7 @@ import 'dart:ui' as ui;
import 'package:crop_image/crop_image.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
@@ -465,9 +466,10 @@ class _CropPageState extends State {
try {
final CropParameters? cropParameters = await _saveImage();
if (cropParameters != null) {
- if (mounted) {
+ /// Checking if the context is still mounted is not enough here
+ SchedulerBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop(cropParameters);
- }
+ });
}
} catch (e) {
await _showExceptionDialog(e);
diff --git a/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart b/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart
index f9dd275ef76..d52d3358313 100644
--- a/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart
+++ b/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart
@@ -23,11 +23,9 @@ double _getSquareSize(final BuildContext context) {
/// Display of the other pictures of a product.
class ProductImageGalleryOtherView extends StatefulWidget {
const ProductImageGalleryOtherView({
- required this.product,
+ super.key,
});
- final Product product;
-
@override
State createState() =>
_ProductImageGalleryOtherViewState();
@@ -35,27 +33,28 @@ class ProductImageGalleryOtherView extends StatefulWidget {
class _ProductImageGalleryOtherViewState
extends State {
- late final Future _loading = _loadOtherPics();
-
- Future _loadOtherPics() async =>
+ Future _loadOtherPics(Product product) async =>
ProductRefresher().silentFetchAndRefresh(
localDatabase: context.read(),
- barcode: widget.product.barcode!,
+ barcode: product.barcode!,
);
@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
+ final Product product = context.watch();
+ context.read();
+
List rawImages = getRawProductImages(
- widget.product,
+ product,
ImageSize.DISPLAY,
);
if (rawImages.isNotEmpty) {
- return _RawGridGallery(widget.product, rawImages);
+ return _RawGridGallery(product, rawImages);
}
final double squareSize = _getSquareSize(context);
return FutureBuilder(
- future: _loading,
+ future: _loadOtherPics(product),
builder: (
final BuildContext context,
final AsyncSnapshot snapshot,
@@ -86,7 +85,7 @@ class _ProductImageGalleryOtherViewState
}
if (rawImages.isNotEmpty) {
return _RawGridGallery(
- fetchedProduct.product ?? widget.product,
+ fetchedProduct.product ?? product,
rawImages,
);
}
@@ -110,6 +109,8 @@ class _RawGridGallery extends StatelessWidget {
Widget build(BuildContext context) {
final double squareSize = _getSquareSize(context);
final ImageSize? imageSize = _computeImageSize(squareSize);
+ final OpenFoodFactsLanguage language =
+ context.read();
return SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
@@ -128,28 +129,35 @@ class _RawGridGallery extends StatelessWidget {
end: index % _columns == 0 ? VERY_SMALL_SPACE : 0.0,
bottom: VERY_SMALL_SPACE,
),
- child: InkWell(
- onTap: () async => Navigator.push(
- context,
- MaterialPageRoute(
- builder: (BuildContext context) {
- return ProductImageOtherPage(
- product: product,
- images: rawImages.reversed.toList(growable: false),
- currentImage: productImage,
- heroTag: heroTag,
- );
- },
+ child: Stack(
+ children: [
+ Positioned.fill(
+ child: ProductImageWidget(
+ productImage: productImage,
+ barcode: product.barcode!,
+ squareSize: squareSize,
+ imageSize: imageSize,
+ heroTag: heroTag,
+ productType: product.productType,
+ ),
+ ),
+ Positioned.fill(
+ child: Material(
+ type: MaterialType.transparency,
+ child: InkWell(
+ borderRadius: ANGULAR_BORDER_RADIUS,
+ onTap: () async => _openOtherPage(
+ context: context,
+ product: product,
+ rawImages: rawImages,
+ productImage: productImage,
+ heroTag: heroTag!,
+ language: language,
+ ),
+ ),
+ ),
),
- ),
- child: ProductImageWidget(
- productImage: productImage,
- barcode: product.barcode!,
- squareSize: squareSize,
- imageSize: imageSize,
- heroTag: heroTag,
- productType: product.productType,
- ),
+ ],
),
);
},
@@ -166,4 +174,28 @@ class _RawGridGallery extends StatelessWidget {
].firstWhereOrNull(
(ImageSize element) => squareSize <= int.parse(element.number),
);
+
+ Future _openOtherPage({
+ required final BuildContext context,
+ required final Product product,
+ required final List rawImages,
+ required final ProductImage productImage,
+ required final String heroTag,
+ required final OpenFoodFactsLanguage language,
+ }) async {
+ await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (BuildContext context) {
+ return ProductImageOtherPage(
+ product: product,
+ language: language,
+ images: rawImages.reversed.toList(growable: false),
+ currentImage: productImage,
+ heroTag: heroTag,
+ );
+ },
+ ),
+ );
+ }
}
diff --git a/packages/smooth_app/lib/pages/image/product_image_other_page.dart b/packages/smooth_app/lib/pages/image/product_image_other_page.dart
index 14fda50e8b8..c2776eee386 100644
--- a/packages/smooth_app/lib/pages/image/product_image_other_page.dart
+++ b/packages/smooth_app/lib/pages/image/product_image_other_page.dart
@@ -3,26 +3,34 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:intl/intl.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
+import 'package:smooth_app/database/transient_file.dart';
import 'package:smooth_app/generic_lib/bottom_sheets/smooth_bottom_sheet.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/helpers/launch_url_helper.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';
+import 'package:smooth_app/pages/crop_parameters.dart';
import 'package:smooth_app/pages/image/product_image_helper.dart';
+import 'package:smooth_app/pages/image/uploaded_image_gallery.dart';
+import 'package:smooth_app/pages/preferences/user_preferences_languages_list.dart';
import 'package:smooth_app/query/product_query.dart';
import 'package:smooth_app/resources/app_icons.dart' as icons;
+import 'package:smooth_app/themes/smooth_theme.dart';
import 'package:smooth_app/themes/smooth_theme_colors.dart';
+import 'package:smooth_app/themes/theme_provider.dart';
import 'package:smooth_app/widgets/smooth_scaffold.dart';
/// Full page display of a raw product image.
class ProductImageOtherPage extends StatefulWidget {
const ProductImageOtherPage({
required this.product,
+ required this.language,
required this.images,
required this.currentImage,
this.heroTag,
});
final Product product;
+ final OpenFoodFactsLanguage language;
final List images;
final ProductImage currentImage;
final String? heroTag;
@@ -44,7 +52,10 @@ class _ProductImageOtherPageState extends State {
@override
Widget build(BuildContext context) {
+ final SmoothColorsThemeExtension extension =
+ context.extension();
final AppLocalizations appLocalizations = AppLocalizations.of(context);
+
return ChangeNotifierProvider.value(
value: _pageController,
child: SmoothScaffold(
@@ -52,6 +63,31 @@ class _ProductImageOtherPageState extends State {
context: context,
title: appLocalizations.edit_product_form_item_photos_title,
product: widget.product,
+ actions: [
+ PopupMenuButton(
+ icon: const Icon(Icons.more_vert),
+ color: context.lightTheme()
+ ? extension.primaryLight
+ : extension.primarySemiDark,
+ menuPadding: EdgeInsets.zero,
+ offset: const Offset(0, 46.0),
+ itemBuilder: (BuildContext context) {
+ return >[
+ PopupMenuItem(
+ value: 1,
+ child: ListTile(
+ leading: const icons.Select.photo(),
+ title: Text(
+ appLocalizations.photo_viewer_action_use_picture_as,
+ style: const TextStyle(fontWeight: FontWeight.w600),
+ ),
+ ),
+ ),
+ ];
+ },
+ onSelected: (int value) => _usePhotoAs(),
+ ),
+ ],
),
body: Stack(
alignment: Alignment.bottomCenter,
@@ -64,6 +100,7 @@ class _ProductImageOtherPageState extends State {
return _ProductImageViewer(
image: image,
barcode: widget.product.barcode!,
+ language: widget.language,
heroTag:
widget.currentImage == image ? widget.heroTag : null,
productType: widget.product.productType,
@@ -83,18 +120,94 @@ class _ProductImageOtherPageState extends State {
),
);
}
+
+ Future _usePhotoAs() async {
+ final AppLocalizations appLocalizations = AppLocalizations.of(context);
+ final SmoothColorsThemeExtension extension =
+ context.extension();
+
+ final List imageFields = [
+ ImageField.FRONT,
+ ImageField.INGREDIENTS,
+ ImageField.NUTRITION,
+ ImageField.PACKAGING,
+ ];
+
+ final Widget existingPictureIcon = icons.Picture.checkAlt(
+ color: extension.success,
+ semanticLabel: appLocalizations.photo_already_exists,
+ );
+ final Widget missingPictureIcon = icons.Picture.error(
+ color: extension.error,
+ semanticLabel: appLocalizations.photo_missing,
+ );
+
+ final ImageField? selectedImageField =
+ await showSmoothListOfChoicesModalSheet(
+ context: context,
+ title: appLocalizations.photo_viewer_use_picture_as_title(
+ Languages().getNameInLanguage(widget.language),
+ ),
+ padding: const EdgeInsetsDirectional.only(
+ start: 15.0,
+ end: 19.0,
+ ),
+ labels: [
+ appLocalizations.photo_field_front,
+ appLocalizations.photo_field_ingredients,
+ appLocalizations.photo_field_nutrition,
+ appLocalizations.photo_field_packaging,
+ ],
+ values: imageFields,
+ prefixIcons: [
+ const icons.Milk.filled(),
+ const icons.Ingredients.alt(),
+ const icons.NutritionFacts(),
+ const icons.Recycling(),
+ ],
+ suffixIcons: imageFields.map((final ImageField imageField) {
+ final bool exists = TransientFile.fromProduct(
+ widget.product,
+ imageField,
+ widget.language,
+ ).isImageAvailable();
+ return exists ? existingPictureIcon : missingPictureIcon;
+ }).toList(growable: false),
+ );
+
+ if (mounted && selectedImageField != null) {
+ final CropParameters? parameters =
+ await UploadedImageGallery.useExistingPhotoFor(
+ context: context,
+ rawImage: widget.currentImage,
+ barcode: widget.product.barcode!,
+ imageField: selectedImageField,
+ isLoggedInMandatory: true,
+ productType: widget.product.productType,
+ language: widget.language,
+ );
+
+ if (mounted && parameters != null) {
+ Navigator.of(context).pop(
+ ProductImagePageResult(parameters, selectedImageField),
+ );
+ }
+ }
+ }
}
class _ProductImageViewer extends StatelessWidget {
const _ProductImageViewer({
required this.image,
required this.barcode,
- this.heroTag,
+ required this.language,
required this.productType,
+ this.heroTag,
});
final ProductImage image;
final String barcode;
+ final OpenFoodFactsLanguage language;
final String? heroTag;
final ProductType? productType;
@@ -373,3 +486,10 @@ class _ProductImagePageIndicator extends StatelessWidget {
);
}
}
+
+class ProductImagePageResult {
+ ProductImagePageResult(this.cropParameters, this.imageField);
+
+ final CropParameters cropParameters;
+ final ImageField imageField;
+}
diff --git a/packages/smooth_app/lib/pages/image/uploaded_image_gallery.dart b/packages/smooth_app/lib/pages/image/uploaded_image_gallery.dart
index 4a4e63185dc..79a11bac190 100644
--- a/packages/smooth_app/lib/pages/image/uploaded_image_gallery.dart
+++ b/packages/smooth_app/lib/pages/image/uploaded_image_gallery.dart
@@ -44,8 +44,11 @@ class UploadedImageGallery extends StatelessWidget {
return SmoothScaffold(
backgroundColor: Colors.black,
+ brightness: Brightness.light,
appBar: SmoothAppBar(
title: Text(appLocalizations.edit_photo_select_existing_all_label),
+ subTitle:
+ Text(appLocalizations.edit_photo_select_existing_all_subtitle),
backgroundColor: Colors.black,
foregroundColor: WHITE_COLOR,
elevation: 0,
@@ -63,55 +66,96 @@ class UploadedImageGallery extends StatelessWidget {
// order by descending ids
index = rawImages.length - 1 - index;
final ProductImage rawImage = rawImages[index];
- return GestureDetector(
- onTap: () async {
- final LocalDatabase localDatabase = context.read();
- final NavigatorState navigatorState = Navigator.of(context);
- final File? imageFile = await downloadImageUrl(
- context,
- rawImage.getUrl(
- barcode,
- imageSize: ImageSize.ORIGINAL,
- uriHelper: ProductQuery.getUriProductHelper(
- productType: productType,
- ),
- ),
- DaoInt(localDatabase),
- );
- if (imageFile == null) {
- return;
- }
- final CropParameters? parameters =
- await navigatorState.push(
- MaterialPageRoute(
- builder: (BuildContext context) => CropPage(
- inputFile: imageFile,
- initiallyDifferent: true,
- isLoggedInMandatory: isLoggedInMandatory,
- cropHelper: ProductCropAgainHelper(
+
+ return Padding(
+ padding: EdgeInsetsDirectional.only(
+ start: VERY_SMALL_SPACE,
+ end: index.isEven ? VERY_SMALL_SPACE : 0.0,
+ bottom: VERY_SMALL_SPACE,
+ ),
+ child: InkWell(
+ borderRadius: ANGULAR_BORDER_RADIUS,
+ onTap: () async {},
+ child: Stack(
+ children: [
+ Positioned.fill(
+ child: ProductImageWidget(
+ productImage: rawImage,
barcode: barcode,
+ squareSize: columnWidth,
productType: productType,
- imageField: imageField,
- imageId: int.parse(rawImage.imgid!),
- language: language,
),
),
- fullscreenDialog: true,
- ),
- );
- if (parameters != null) {
- navigatorState.pop();
- }
- },
- child: ProductImageWidget(
- productImage: rawImage,
- barcode: barcode,
- squareSize: columnWidth,
- productType: productType,
+ Positioned.fill(
+ child: Material(
+ type: MaterialType.transparency,
+ child: InkWell(
+ borderRadius: ANGULAR_BORDER_RADIUS,
+ onTap: () async =>
+ Navigator.of(context).pop(
+ await useExistingPhotoFor(
+ context: context,
+ rawImage: rawImage,
+ barcode: barcode,
+ imageField: imageField,
+ isLoggedInMandatory: isLoggedInMandatory,
+ productType: productType,
+ language: language,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
),
);
},
),
);
}
+
+ static Future useExistingPhotoFor({
+ required final BuildContext context,
+ required final ProductImage rawImage,
+ required final String barcode,
+ required final ImageField imageField,
+ required final bool isLoggedInMandatory,
+ required final ProductType? productType,
+ required final OpenFoodFactsLanguage language,
+ }) async {
+ final LocalDatabase localDatabase = context.read();
+ final File? imageFile = await downloadImageUrl(
+ context,
+ rawImage.getUrl(
+ barcode,
+ imageSize: ImageSize.ORIGINAL,
+ uriHelper: ProductQuery.getUriProductHelper(
+ productType: productType,
+ ),
+ ),
+ DaoInt(localDatabase),
+ );
+ if (imageFile == null || !context.mounted) {
+ return null;
+ }
+
+ return Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (BuildContext context) => CropPage(
+ inputFile: imageFile,
+ initiallyDifferent: true,
+ isLoggedInMandatory: isLoggedInMandatory,
+ cropHelper: ProductCropAgainHelper(
+ barcode: barcode,
+ productType: productType,
+ imageField: imageField,
+ imageId: int.parse(rawImage.imgid!),
+ language: language,
+ ),
+ ),
+ fullscreenDialog: true,
+ ),
+ );
+ }
}
diff --git a/packages/smooth_app/lib/pages/image_crop_page.dart b/packages/smooth_app/lib/pages/image_crop_page.dart
index aed3920e65b..8621ca75516 100644
--- a/packages/smooth_app/lib/pages/image_crop_page.dart
+++ b/packages/smooth_app/lib/pages/image_crop_page.dart
@@ -280,6 +280,7 @@ Future confirmAndUploadNewPicture(
required final ProductType? productType,
required final OpenFoodFactsLanguage language,
required final bool isLoggedInMandatory,
+ final UserPictureSource? forcedSource,
}) async =>
confirmAndUploadNewImage(
context,
@@ -290,6 +291,7 @@ Future confirmAndUploadNewPicture(
productType: productType,
),
isLoggedInMandatory: isLoggedInMandatory,
+ forcedSource: forcedSource,
);
/// Lets the user pick a picture, crop it, and save it.
diff --git a/packages/smooth_app/lib/pages/product/edit_product_page.dart b/packages/smooth_app/lib/pages/product/edit_product_page.dart
index 01a7fc4a752..b7467d23b4e 100644
--- a/packages/smooth_app/lib/pages/product/edit_product_page.dart
+++ b/packages/smooth_app/lib/pages/product/edit_product_page.dart
@@ -10,7 +10,6 @@ import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_list_tile_card.dart';
-import 'package:smooth_app/generic_lib/widgets/svg_icon.dart';
import 'package:smooth_app/helpers/analytics_helper.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';
import 'package:smooth_app/pages/onboarding/currency_selector_helper.dart';
@@ -189,10 +188,7 @@ class _EditProductPageState extends State with UpToDateMixin {
),
if (upToDateProduct.productType != ProductType.product)
_ListTitleItem(
- leading: SvgIcon(
- 'assets/cacheTintable/ingredients.svg',
- dontAddColor: context.lightTheme(),
- ),
+ leading: const icons.Ingredients.alt(),
title:
appLocalizations.edit_product_form_item_ingredients_title,
onTap: () async => ProductFieldOcrIngredientEditor().edit(
@@ -235,10 +231,7 @@ class _EditProductPageState extends State with UpToDateMixin {
}),
_getSimpleListTileItem(SimpleInputPageLabelHelper()),
_ListTitleItem(
- leading: SvgIcon(
- 'assets/cacheTintable/packaging.svg',
- dontAddColor: context.lightTheme(),
- ),
+ leading: const icons.Packaging(),
title: appLocalizations.edit_packagings_title,
onTap: () async => ProductFieldPackagingEditor().edit(
context: context,
@@ -246,7 +239,7 @@ class _EditProductPageState extends State with UpToDateMixin {
),
),
_ListTitleItem(
- leading: const Icon(Icons.recycling),
+ leading: const icons.Recycling(),
title: appLocalizations.edit_product_form_item_packaging_title,
onTap: () async => ProductFieldOcrPackagingEditor().edit(
context: context,
@@ -427,6 +420,7 @@ class _ProductBarcodeState extends State<_ProductBarcode> {
horizontal: screenSize.width / 4,
vertical: SMALL_SPACE,
),
+ color: context.lightTheme() ? Colors.black : Colors.white,
barcode: widget.product.barcode!,
onInvalidBarcode: () {
if (!_isAnInvalidBarcode) {
diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart
index 9f5367098d2..f9cca30f58c 100644
--- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart
+++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart
@@ -6,16 +6,21 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/cards/product_cards/smooth_product_image.dart';
+import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/database/transient_file.dart';
+import 'package:smooth_app/generic_lib/bottom_sheets/smooth_bottom_sheet.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/helpers/image_field_extension.dart';
+import 'package:smooth_app/pages/crop_parameters.dart';
import 'package:smooth_app/pages/image/product_image_helper.dart';
import 'package:smooth_app/pages/product/gallery_view/product_image_gallery_view.dart';
+import 'package:smooth_app/pages/product/product_image_server_button.dart';
import 'package:smooth_app/pages/product/product_image_swipeable_view.dart';
import 'package:smooth_app/resources/app_animations.dart';
import 'package:smooth_app/resources/app_icons.dart';
import 'package:smooth_app/themes/smooth_theme.dart';
import 'package:smooth_app/themes/smooth_theme_colors.dart';
+import 'package:smooth_app/themes/theme_provider.dart';
class ImageGalleryPhotoRow extends StatefulWidget {
const ImageGalleryPhotoRow({
@@ -77,6 +82,7 @@ class _ImageGalleryPhotoRowState extends State {
child: Material(
elevation: 1.0,
type: MaterialType.card,
+ shadowColor: Colors.white,
color: extension.primaryBlack,
borderRadius: ANGULAR_BORDER_RADIUS,
child: InkWell(
@@ -87,6 +93,11 @@ class _ImageGalleryPhotoRowState extends State {
transientFile: transientFile,
initialImageIndex: widget.position,
),
+ onLongPress: () => _onLongTap(
+ context: context,
+ product: product,
+ transientFile: transientFile,
+ ),
child: ClipRRect(
borderRadius: ANGULAR_BORDER_RADIUS,
child: Column(
@@ -159,6 +170,11 @@ class _ImageGalleryPhotoRowState extends State {
},
),
),
+ // Border
+ const Positioned.fill(
+ child: _PhotoBorder(),
+ ),
+ // Upload animation
if (_temporaryFile != null ||
transientFile.isImageAvailable() &&
!transientFile.isServerImage())
@@ -189,16 +205,90 @@ class _ImageGalleryPhotoRowState extends State {
initialImageIndex: initialImageIndex,
);
} else {
- _temporaryFile = await ProductImageGalleryView.takePicture(
+ await _takePicture(
context: context,
product: product,
- language: widget.language,
- imageField: widget.imageField,
+ pictureSource: UserPictureSource.SELECT,
);
+ }
+ }
- if (_temporaryFile != null) {
- setState(() {});
- }
+ Future _onLongTap({
+ required final BuildContext context,
+ required final Product product,
+ required final TransientFile transientFile,
+ }) async {
+ final SmoothColorsThemeExtension extension =
+ context.extension();
+ final bool lightTheme = context.lightTheme(listen: false);
+ final bool imageAvailable = transientFile.isImageAvailable();
+
+ final AppLocalizations appLocalizations = AppLocalizations.of(context);
+
+ final _PhotoRowActions? action =
+ await showSmoothListOfChoicesModalSheet<_PhotoRowActions>(
+ context: context,
+ title: imageAvailable
+ ? appLocalizations.product_image_action_replace_photo(
+ widget.imageField.getProductImageTitle(appLocalizations))
+ : appLocalizations.product_image_action_add_photo(
+ widget.imageField.getProductImageTitle(appLocalizations)),
+ values: _PhotoRowActions.values,
+ labels: [
+ if (imageAvailable)
+ appLocalizations.product_image_action_take_new_picture
+ else
+ appLocalizations.product_image_action_take_picture,
+ appLocalizations.product_image_action_from_gallery,
+ appLocalizations.product_image_action_choose_existing_photo,
+ ],
+ prefixIconTint:
+ lightTheme ? extension.primaryDark : extension.primaryMedium,
+ prefixIcons: [
+ const Icon(Icons.camera),
+ const Icon(Icons.perm_media_rounded),
+ const Icon(Icons.image_search_rounded),
+ ],
+ );
+
+ if (!context.mounted || action == null) {
+ return;
+ }
+
+ return switch (action) {
+ _PhotoRowActions.takePicture => _takePicture(
+ context: context,
+ product: product,
+ pictureSource: UserPictureSource.CAMERA,
+ ),
+ _PhotoRowActions.selectFromGallery => _takePicture(
+ context: context,
+ product: product,
+ pictureSource: UserPictureSource.GALLERY,
+ ),
+ _PhotoRowActions.selectFromProductPhotos =>
+ _selectPictureFromProductGallery(
+ context: context,
+ product: product,
+ ),
+ };
+ }
+
+ Future _takePicture({
+ required final BuildContext context,
+ required final Product product,
+ required final UserPictureSource pictureSource,
+ }) async {
+ _temporaryFile = await ProductImageGalleryView.takePicture(
+ context: context,
+ product: product,
+ language: widget.language,
+ imageField: widget.imageField,
+ pictureSource: pictureSource,
+ );
+
+ if (_temporaryFile != null) {
+ setState(() {});
}
}
@@ -219,6 +309,26 @@ class _ImageGalleryPhotoRowState extends State {
),
);
+ Future _selectPictureFromProductGallery({
+ required final BuildContext context,
+ required final Product product,
+ }) async {
+ final CropParameters? parameters =
+ await ProductImageServerButton.selectImageFromGallery(
+ context: context,
+ product: product,
+ imageField: widget.imageField,
+ language: widget.language,
+ isLoggedInMandatory: true,
+ );
+
+ if (parameters?.smallCroppedFile != null) {
+ setState(() {
+ _temporaryFile = parameters!.smallCroppedFile;
+ });
+ }
+ }
+
TransientFile _getTransientFile(
final Product product,
final ImageField imageField,
@@ -230,6 +340,12 @@ class _ImageGalleryPhotoRowState extends State {
);
}
+enum _PhotoRowActions {
+ takePicture,
+ selectFromGallery,
+ selectFromProductPhotos,
+}
+
class _PhotoRowIndicator extends StatelessWidget {
const _PhotoRowIndicator({
required this.transientFile,
@@ -286,3 +402,33 @@ class _PhotoRowIndicator extends StatelessWidget {
}
}
}
+
+class _PhotoBorder extends StatelessWidget {
+ const _PhotoBorder();
+
+ @override
+ Widget build(BuildContext context) {
+ final SmoothColorsThemeExtension extension =
+ context.extension();
+
+ final bool lightTheme = context.lightTheme();
+
+ final BorderSide borderSide = BorderSide(
+ width: lightTheme ? 1.0 : 0.5,
+ color: lightTheme ? extension.primaryMedium : extension.primarySemiDark,
+ );
+
+ return DecoratedBox(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.vertical(
+ bottom: ANGULAR_RADIUS,
+ ),
+ border: Border(
+ right: borderSide,
+ left: borderSide,
+ bottom: borderSide,
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_tabs.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_tabs.dart
index 095bfe538d5..73cb7837091 100644
--- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_tabs.dart
+++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_tabs.dart
@@ -70,7 +70,8 @@ class _ProductImageGalleryTabBarState extends State
) {
if (provider.value.languages == null) {
return const Center(
- child: CircularProgressIndicator.adaptive());
+ child: CircularProgressIndicator.adaptive(),
+ );
}
/// We need a Stack to have to tab bar shadow below the button
@@ -116,16 +117,17 @@ class _ProductImageGalleryTabBarState extends State
}
void _updateListener() {
- if (_provider.value.languages != null) {
+ final _ImageGalleryLanguagesState value = _provider.value;
+
+ if (value.languages != null) {
final int initialIndex = _tabController?.index ?? -1;
- final int newIndex = _provider.value.selectedLanguage != null
- ? _provider.value.languages!
- .indexOf(_provider.value.selectedLanguage!)
+ final int newIndex = value.selectedLanguage != null
+ ? value.languages!.indexOf(value.selectedLanguage!)
: initialIndex;
- if (_tabController?.length != _provider.value.languages!.length) {
+ if (_tabController?.length != value.languages!.length) {
_tabController = TabController(
- length: _provider.value.languages!.length,
+ length: value.languages!.length,
vsync: this,
initialIndex: newIndex >= 0 ? newIndex : 0,
);
@@ -136,8 +138,8 @@ class _ProductImageGalleryTabBarState extends State
});
}
- if (newIndex >= 0 && initialIndex != newIndex) {
- widget.onTabChanged.call(_provider.value.selectedLanguage!);
+ if (value.initialValue || (newIndex >= 0 && initialIndex != newIndex)) {
+ widget.onTabChanged.call(value.selectedLanguage!);
}
}
}
@@ -150,6 +152,7 @@ class _ImageGalleryAddLanguageButton extends StatelessWidget {
Widget build(BuildContext context) {
final SmoothColorsThemeExtension theme =
context.extension();
+ final bool lightTheme = context.lightTheme();
final BorderRadius borderRadius = BorderRadiusHelper.fromDirectional(
context: context,
@@ -167,8 +170,13 @@ class _ImageGalleryAddLanguageButton extends StatelessWidget {
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: borderRadius,
- color:
- context.lightTheme() ? theme.primaryDark : theme.primaryNormal,
+ border: Border(
+ bottom: BorderSide(
+ color: lightTheme ? theme.primarySemiDark : theme.primaryDark,
+ width: lightTheme ? 1.5 : 2.0,
+ ),
+ ),
+ color: lightTheme ? theme.primaryDark : theme.primaryNormal,
),
child: Material(
type: MaterialType.transparency,
@@ -227,18 +235,19 @@ class _ImageGalleryLanguagesProvider
void attachProduct(final Product product) {
this.product = product;
- refreshLanguages();
+ refreshLanguages(initial: true);
}
- void refreshLanguages() {
+ void refreshLanguages({bool initial = false}) {
final List imageLanguages = {
...getProductImageLanguages(product, ImageField.FRONT),
...getProductImageLanguages(product, ImageField.INGREDIENTS),
...getProductImageLanguages(product, ImageField.NUTRITION),
...getProductImageLanguages(product, ImageField.PACKAGING),
}.toList(growable: false)
- ..sort((final OpenFoodFactsLanguage a, final OpenFoodFactsLanguage b) =>
- a.name.compareTo(b.name));
+ ..sort((final OpenFoodFactsLanguage a, final OpenFoodFactsLanguage b) {
+ return a.code.compareTo(b.code);
+ });
/// The main language is always the first, then the user one
final OpenFoodFactsLanguage userLanguage = ProductQuery.getLanguage();
@@ -263,6 +272,7 @@ class _ImageGalleryLanguagesProvider
value = _ImageGalleryLanguagesState(
languages: languages,
selectedLanguage: mainLanguage ?? userLanguage,
+ initialValue: initial,
);
}
@@ -300,15 +310,19 @@ class _ImageGalleryLanguagesState {
required this.languages,
required this.selectedLanguage,
this.hasNewLanguage = false,
+ this.initialValue = false,
}) : assert(
- selectedLanguage == null || languages!.contains(selectedLanguage));
+ selectedLanguage == null || languages!.contains(selectedLanguage),
+ );
const _ImageGalleryLanguagesState.empty()
: languages = null,
selectedLanguage = null,
- hasNewLanguage = false;
+ hasNewLanguage = false,
+ initialValue = false;
final List? languages;
final OpenFoodFactsLanguage? selectedLanguage;
+ final bool initialValue;
final bool hasNewLanguage;
}
diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_view.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_view.dart
index 42cba79d23a..e50f6295fb1 100644
--- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_view.dart
+++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_view.dart
@@ -1,17 +1,21 @@
import 'dart:io';
import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
+import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/data_models/up_to_date_mixin.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
+import 'package:smooth_app/generic_lib/duration_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_app_logo.dart';
import 'package:smooth_app/helpers/analytics_helper.dart';
import 'package:smooth_app/helpers/border_radius_helper.dart';
import 'package:smooth_app/helpers/image_field_extension.dart';
+import 'package:smooth_app/helpers/num_utils.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';
import 'package:smooth_app/helpers/ui_helpers.dart';
import 'package:smooth_app/pages/crop_parameters.dart';
@@ -24,9 +28,9 @@ import 'package:smooth_app/query/product_query.dart';
import 'package:smooth_app/resources/app_icons.dart' as icons;
import 'package:smooth_app/themes/smooth_theme.dart';
import 'package:smooth_app/themes/smooth_theme_colors.dart';
-import 'package:smooth_app/themes/theme_provider.dart';
import 'package:smooth_app/widgets/slivers.dart';
import 'package:smooth_app/widgets/smooth_scaffold.dart';
+import 'package:smooth_app/widgets/widget_height.dart';
/// Display of the main 4 pictures of a product, with edit options.
class ProductImageGalleryView extends StatefulWidget {
@@ -45,6 +49,7 @@ class ProductImageGalleryView extends StatefulWidget {
required Product product,
required ImageField imageField,
required OpenFoodFactsLanguage language,
+ UserPictureSource? pictureSource,
}) async {
AnalyticsHelper.trackProductEdit(
AnalyticsEditEvents.photos,
@@ -59,6 +64,7 @@ class ProductImageGalleryView extends StatefulWidget {
language: language,
isLoggedInMandatory: true,
productType: product.productType,
+ forcedSource: pictureSource,
);
return cropParameters?.smallCroppedFile;
@@ -67,6 +73,7 @@ class ProductImageGalleryView extends StatefulWidget {
class _ProductImageGalleryViewState extends State
with UpToDateMixin {
+ final ScrollController _scrollController = ScrollController();
late OpenFoodFactsLanguage _language;
late final List _mainImageFields;
bool _clickedOtherPictureButton = false;
@@ -89,138 +96,140 @@ class _ProductImageGalleryViewState extends State
return MultiProvider(
providers: >[
- Provider(
- create: (_) => upToDateProduct,
- ),
+ Provider.value(value: upToDateProduct),
Provider.value(
value: _language,
),
],
- child: Provider.value(
- value: upToDateProduct,
- child: SmoothSharedAnimationController(
- child: SmoothScaffold(
- appBar: buildEditProductAppBar(
- context: context,
- title: appLocalizations.edit_product_form_item_photos_title,
- product: upToDateProduct,
- bottom: ProductImageGalleryTabBar(
- onTabChanged: (final OpenFoodFactsLanguage language) =>
- onNextFrame(
- () => setState(() => _language = language),
+ child: SmoothSharedAnimationController(
+ child: SmoothScaffold(
+ appBar: buildEditProductAppBar(
+ context: context,
+ title: appLocalizations.edit_product_form_item_photos_title,
+ product: upToDateProduct,
+ bottom: ProductImageGalleryTabBar(
+ onTabChanged: (final OpenFoodFactsLanguage language) =>
+ onNextFrame(
+ () => setState(
+ () {
+ _language = language;
+ _scrollController.animateTo(
+ 0.0,
+ duration: SmoothAnimationsDuration.short,
+ curve: Curves.easeInOut,
+ );
+ },
),
- padding: const EdgeInsetsDirectional.only(start: 55.0),
),
+ padding: const EdgeInsetsDirectional.only(start: 55.0),
),
- body: Stack(
- children: [
- Positioned.fill(
- child: Column(
- children: [
- Expanded(
- child: RefreshIndicator(
- onRefresh: () async =>
- ProductRefresher().fetchAndRefresh(
- barcode: barcode,
- context: context,
- ),
- child: CustomScrollView(
- slivers: [
- SliverPadding(
- padding: const EdgeInsetsDirectional.only(
- top: VERY_SMALL_SPACE,
+ ),
+ body: Stack(
+ children: [
+ Positioned.fill(
+ child: Column(
+ children: [
+ Expanded(
+ child: RefreshIndicator(
+ onRefresh: () async =>
+ ProductRefresher().fetchAndRefresh(
+ barcode: barcode,
+ context: context,
+ ),
+ child: CustomScrollView(
+ controller: _scrollController,
+ slivers: [
+ SliverPadding(
+ padding: const EdgeInsetsDirectional.only(
+ top: VERY_SMALL_SPACE,
+ ),
+ sliver: SliverGrid(
+ gridDelegate:
+ SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight(
+ crossAxisCount: 2,
+ height: (MediaQuery.sizeOf(context).width /
+ 2.15) +
+ ImageGalleryPhotoRow.itemHeight,
),
- sliver: SliverGrid(
- gridDelegate:
- SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight(
- crossAxisCount: 2,
- height: (MediaQuery.sizeOf(context).width /
- 2.15) +
- ImageGalleryPhotoRow.itemHeight,
- ),
- delegate: SliverChildBuilderDelegate(
- (BuildContext context, int index) {
- return Padding(
- padding: EdgeInsetsDirectional.only(
- top: VERY_SMALL_SPACE,
- start: index.isOdd
- ? VERY_SMALL_SPACE / 2
- : VERY_SMALL_SPACE,
- end: index.isOdd
- ? VERY_SMALL_SPACE
- : VERY_SMALL_SPACE / 2,
- ),
- child: ImageGalleryPhotoRow(
- key: Key(
- '${_mainImageFields[index]}_${_language.offTag}}',
- ),
- position: index,
- imageField: _mainImageFields[index],
- language: _language,
+ delegate: SliverChildBuilderDelegate(
+ (BuildContext context, int index) {
+ return Padding(
+ padding: EdgeInsetsDirectional.only(
+ top: VERY_SMALL_SPACE,
+ start: index.isOdd
+ ? VERY_SMALL_SPACE / 2
+ : VERY_SMALL_SPACE,
+ end: index.isOdd
+ ? VERY_SMALL_SPACE
+ : VERY_SMALL_SPACE / 2,
+ ),
+ child: ImageGalleryPhotoRow(
+ key: Key(
+ '${_mainImageFields[index]}_${_language.offTag}}',
),
- );
- },
- childCount: _mainImageFields.length,
- addAutomaticKeepAlives: false,
- ),
+ position: index,
+ imageField: _mainImageFields[index],
+ language: _language,
+ ),
+ );
+ },
+ childCount: _mainImageFields.length,
+ addAutomaticKeepAlives: false,
),
),
- SliverPadding(
- padding: const EdgeInsetsDirectional.symmetric(
- vertical: MEDIUM_SPACE,
- horizontal: SMALL_SPACE,
- ),
- sliver: SliverToBoxAdapter(
- child: Text(
- appLocalizations.more_photos,
- style: Theme.of(context)
- .textTheme
- .displayMedium,
- ),
+ ),
+ SliverPadding(
+ padding: const EdgeInsetsDirectional.symmetric(
+ vertical: MEDIUM_SPACE,
+ horizontal: SMALL_SPACE,
+ ),
+ sliver: SliverToBoxAdapter(
+ child: Text(
+ appLocalizations.more_photos,
+ style:
+ Theme.of(context).textTheme.displayMedium,
),
),
- if (_shouldDisplayRawGallery())
- ProductImageGalleryOtherView(
- product: upToDateProduct,
- )
- else
- SliverToBoxAdapter(
- child: Padding(
- padding: const EdgeInsets.all(SMALL_SPACE),
- child: SmoothLargeButtonWithIcon(
- text: appLocalizations
- .view_more_photo_button,
- icon: Icons.photo_camera_rounded,
- onPressed: () => setState(
- () => _clickedOtherPictureButton = true,
- ),
+ ),
+ if (_shouldDisplayRawGallery())
+ const ProductImageGalleryOtherView()
+ else
+ SliverToBoxAdapter(
+ child: Padding(
+ padding: const EdgeInsets.all(SMALL_SPACE),
+ child: SmoothLargeButtonWithIcon(
+ text:
+ appLocalizations.view_more_photo_button,
+ icon: Icons.photo_camera_rounded,
+ onPressed: () => setState(
+ () => _clickedOtherPictureButton = true,
),
),
),
- SliverToBoxAdapter(
- child: SizedBox(
- height:
- MediaQuery.viewPaddingOf(context).bottom +
- (VERY_LARGE_SPACE * 2.5),
- ),
- )
- ],
- ),
+ ),
+ SliverToBoxAdapter(
+ child: SizedBox(
+ height:
+ MediaQuery.viewPaddingOf(context).bottom +
+ (VERY_LARGE_SPACE * 2.5),
+ ),
+ )
+ ],
),
),
- ],
- ),
- ),
- Positioned.directional(
- textDirection: Directionality.of(context),
- bottom: 0.0,
- end: 0.0,
- child: const _ProductImageGalleryFooterButton(),
+ ),
+ ],
),
- ],
- ),
- persistentFooterAlignment: AlignmentDirectional.bottomEnd,
+ ),
+ Positioned.directional(
+ textDirection: Directionality.of(context),
+ bottom: 0.0,
+ end: 0.0,
+ child: const _ProductImageGalleryFooterButton(),
+ ),
+ ],
),
+ persistentFooterAlignment: AlignmentDirectional.bottomEnd,
),
),
);
@@ -231,9 +240,41 @@ class _ProductImageGalleryViewState extends State
(upToDateProduct.getRawImages()?.isNotEmpty == true);
}
-class _ProductImageGalleryFooterButton extends StatelessWidget {
+class _ProductImageGalleryFooterButton extends StatefulWidget {
const _ProductImageGalleryFooterButton();
+ @override
+ State<_ProductImageGalleryFooterButton> createState() =>
+ _ProductImageGalleryFooterButtonState();
+}
+
+class _ProductImageGalleryFooterButtonState
+ extends State<_ProductImageGalleryFooterButton>
+ with SingleTickerProviderStateMixin {
+ late AnimationController _controller;
+ Animation? _animation;
+
+ ScrollNotificationObserverState? _scrollNotificationObserver;
+ double _scrollInitialPosition = 0.0;
+ ScrollDirection _scrollDirection = ScrollDirection.idle;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = AnimationController(
+ vsync: this,
+ duration: const Duration(milliseconds: 200),
+ )..addListener(() => setState(() {}));
+ }
+
+ @override
+ void didChangeDependencies() {
+ super.didChangeDependencies();
+ _scrollNotificationObserver?.removeListener(_handleScrollNotification);
+ _scrollNotificationObserver = ScrollNotificationObserver.maybeOf(context);
+ _scrollNotificationObserver?.addListener(_handleScrollNotification);
+ }
+
@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
@@ -249,58 +290,72 @@ class _ProductImageGalleryFooterButton extends StatelessWidget {
button: true,
label: appLocalizations.add_photo_button_label,
excludeSemantics: true,
- child: DecoratedBox(
- decoration: BoxDecoration(
- color:
- context.lightTheme() ? theme.primaryMedium : theme.primaryNormal,
- borderRadius: borderRadius,
- boxShadow: [
- BoxShadow(
- color: Theme.of(context).shadowColor.withOpacity(0.3),
- blurRadius: 5.0,
- spreadRadius: 1.0,
- offset: Offset.zero,
- ),
- ],
- ),
- child: Material(
- type: MaterialType.transparency,
- child: InkWell(
+ child: Transform.translate(
+ offset: Offset(_animation?.value ?? 0.0, 0.0),
+ child: DecoratedBox(
+ decoration: BoxDecoration(
+ color: theme.primaryMedium,
borderRadius: borderRadius,
- onTap: () async => ProductImageGalleryView.takePicture(
- context: context,
- product: context.read(),
- imageField: ImageField.OTHER,
- language: context.read(),
- ),
- child: Padding(
- padding: EdgeInsetsDirectional.only(
- top: LARGE_SPACE,
- start: LARGE_SPACE,
- end: LARGE_SPACE,
- bottom:
- MediaQuery.viewPaddingOf(context).bottom + VERY_SMALL_SPACE,
+ boxShadow: [
+ BoxShadow(
+ color: Theme.of(context).shadowColor.withOpacity(0.3),
+ blurRadius: 5.0,
+ spreadRadius: 1.0,
+ offset: Offset.zero,
),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- const icons.Add(
- color: Colors.black,
- ),
- const SizedBox(width: BALANCED_SPACE),
- Padding(
- padding: const EdgeInsetsDirectional.only(bottom: 2.0),
- child: Text(
- appLocalizations.add_photo_button_label,
- style: const TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 15.0,
- color: Colors.black,
+ ],
+ ),
+ child: Material(
+ type: MaterialType.transparency,
+ child: InkWell(
+ borderRadius: borderRadius,
+ onTap: () async => ProductImageGalleryView.takePicture(
+ context: context,
+ product: context.read(),
+ imageField: ImageField.OTHER,
+ language: context.read(),
+ ),
+ child: Padding(
+ padding: EdgeInsetsDirectional.only(
+ top: LARGE_SPACE,
+ start: LARGE_SPACE,
+ end: LARGE_SPACE,
+ bottom: MediaQuery.viewPaddingOf(context).bottom +
+ VERY_SMALL_SPACE,
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ const icons.Add(
+ color: Colors.black,
+ ),
+ Offstage(
+ offstage: _animation?.value == 1.0,
+ child: MeasureSize(
+ onChange: _onTextSizeAvailable,
+ child: Opacity(
+ opacity: 1.0 -
+ _controller.value.progressAndClamp(0.4, 1.0, 1.0),
+ child: Padding(
+ padding: const EdgeInsetsDirectional.only(
+ start: BALANCED_SPACE,
+ bottom: 2.0,
+ ),
+ child: Text(
+ appLocalizations.add_photo_button_label,
+ style: const TextStyle(
+ fontWeight: FontWeight.bold,
+ fontSize: 15.0,
+ color: Colors.black,
+ ),
+ ),
+ ),
+ ),
),
),
- ),
- ],
+ ],
+ ),
),
),
),
@@ -308,4 +363,44 @@ class _ProductImageGalleryFooterButton extends StatelessWidget {
),
);
}
+
+ void _onTextSizeAvailable(Size size) {
+ _animation ??= Tween(
+ begin: 0.0,
+ end: size.width,
+ ).animate(
+ CurvedAnimation(
+ parent: _controller,
+ curve: Curves.fastOutSlowIn,
+ ),
+ );
+ }
+
+ void _handleScrollNotification(ScrollNotification notification) {
+ if (notification is ScrollStartNotification) {
+ _scrollInitialPosition = notification.metrics.extentBefore;
+ _scrollDirection = ScrollDirection.idle;
+ } else if (notification is ScrollUpdateNotification) {
+ final double diff =
+ _scrollInitialPosition - notification.metrics.extentBefore;
+ if (diff > 10 && _scrollDirection != ScrollDirection.reverse) {
+ _scrollDirection = ScrollDirection.reverse;
+ _scrollInitialPosition = notification.metrics.extentBefore;
+ _controller.reverse();
+ } else if (diff < -10 && _scrollDirection != ScrollDirection.forward) {
+ _scrollDirection = ScrollDirection.forward;
+ _scrollInitialPosition = notification.metrics.extentBefore;
+ _controller.forward();
+ }
+ }
+ }
+
+ @override
+ void dispose() {
+ if (_scrollNotificationObserver != null) {
+ _scrollNotificationObserver!.removeListener(_handleScrollNotification);
+ _scrollNotificationObserver = null;
+ }
+ super.dispose();
+ }
}
diff --git a/packages/smooth_app/lib/pages/product/product_image_server_button.dart b/packages/smooth_app/lib/pages/product/product_image_server_button.dart
index cdc09431a3a..09acc134df2 100644
--- a/packages/smooth_app/lib/pages/product/product_image_server_button.dart
+++ b/packages/smooth_app/lib/pages/product/product_image_server_button.dart
@@ -6,6 +6,7 @@ import 'package:smooth_app/database/dao_product.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';
+import 'package:smooth_app/pages/crop_parameters.dart';
import 'package:smooth_app/pages/image/uploaded_image_gallery.dart';
import 'package:smooth_app/pages/product/common/product_refresher.dart';
import 'package:smooth_app/pages/product/product_image_button.dart';
@@ -26,7 +27,7 @@ class ProductImageServerButton extends ProductImageButton {
bool isHidden() => !_hasServerImages;
@override
- IconData getIconData() => Icons.image_search;
+ IconData getIconData() => Icons.image_search_rounded;
@override
String getLabel(final AppLocalizations appLocalizations) =>
@@ -34,16 +35,32 @@ class ProductImageServerButton extends ProductImageButton {
@override
Future action(final BuildContext context) async {
+ await selectImageFromGallery(
+ context: context,
+ product: product,
+ imageField: imageField,
+ language: language,
+ isLoggedInMandatory: isLoggedInMandatory,
+ );
+ }
+
+ static Future selectImageFromGallery({
+ required final BuildContext context,
+ required final Product product,
+ required final ImageField imageField,
+ required final OpenFoodFactsLanguage language,
+ required final bool isLoggedInMandatory,
+ }) async {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
if (!await ProductRefresher().checkIfLoggedIn(
context,
isLoggedInMandatory: isLoggedInMandatory,
)) {
- return;
+ return null;
}
if (!context.mounted) {
- return;
+ return null;
}
List rawImages = getRawProductImages(
@@ -51,30 +68,34 @@ class ProductImageServerButton extends ProductImageButton {
ImageSize.DISPLAY,
);
if (rawImages.isNotEmpty) {
- await _openGallery(
+ return _openGallery(
context: context,
rawImages: rawImages,
productType: product.productType,
+ barcode: product.barcode!,
+ imageField: imageField,
+ language: language,
+ isLoggedInMandatory: isLoggedInMandatory,
);
- return;
}
final bool fetched = await ProductRefresher().fetchAndRefresh(
- barcode: barcode,
+ barcode: product.barcode!,
context: context,
);
if (!fetched) {
- return;
+ return null;
}
if (!context.mounted) {
- return;
+ return null;
}
- final Product? latestProduct =
- await DaoProduct(context.read()).get(barcode);
+ final Product? latestProduct = await DaoProduct(
+ context.read(),
+ ).get(product.barcode!);
if (!context.mounted) {
- return;
+ return null;
}
if (latestProduct != null) {
// very likely
@@ -97,23 +118,31 @@ class ProductImageServerButton extends ProductImageButton {
),
),
);
- return;
+ return null;
}
- await _openGallery(
+ return _openGallery(
context: context,
rawImages: rawImages,
productType: product.productType,
+ barcode: product.barcode!,
+ imageField: imageField,
+ language: language,
+ isLoggedInMandatory: isLoggedInMandatory,
);
}
- Future _openGallery({
+ static Future _openGallery({
required final BuildContext context,
required final List rawImages,
required final ProductType? productType,
+ required final String barcode,
+ required final ImageField imageField,
+ required final OpenFoodFactsLanguage language,
+ required final bool isLoggedInMandatory,
}) =>
- Navigator.push(
+ Navigator.push(
context,
- MaterialPageRoute(
+ MaterialPageRoute(
builder: (BuildContext context) => UploadedImageGallery(
barcode: barcode,
rawImages: rawImages,
diff --git a/packages/smooth_app/lib/pages/product/product_list_helper.dart b/packages/smooth_app/lib/pages/product/product_list_helper.dart
index 2c3314db017..2ed19e1d381 100644
--- a/packages/smooth_app/lib/pages/product/product_list_helper.dart
+++ b/packages/smooth_app/lib/pages/product/product_list_helper.dart
@@ -15,6 +15,7 @@ import 'package:smooth_app/helpers/haptic_feedback_helper.dart';
import 'package:smooth_app/helpers/provider_helper.dart';
import 'package:smooth_app/pages/product_list_user_dialog_helper.dart';
import 'package:smooth_app/resources/app_icons.dart';
+import 'package:smooth_app/themes/smooth_theme.dart';
import 'package:smooth_app/themes/smooth_theme_colors.dart';
import 'package:smooth_app/themes/theme_provider.dart';
import 'package:smooth_app/widgets/smooth_checkbox.dart';
@@ -87,8 +88,8 @@ class _AddToProductListNoListAvailable extends StatelessWidget {
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
- color: Theme.of(context)
- .extension()!
+ color: context
+ .extension()
.secondaryLight,
),
padding: const EdgeInsets.all(VERY_LARGE_SPACE),
diff --git a/packages/smooth_app/lib/pages/product/product_page/new_product_header.dart b/packages/smooth_app/lib/pages/product/product_page/new_product_header.dart
index 382d066c9eb..80f18f1cb1a 100644
--- a/packages/smooth_app/lib/pages/product/product_page/new_product_header.dart
+++ b/packages/smooth_app/lib/pages/product/product_page/new_product_header.dart
@@ -207,7 +207,7 @@ class _ProductHeaderName extends StatelessWidget {
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontWeight: FontWeight.bold,
- fontSize: 18.0,
+ fontSize: 17.0,
height: 0.9,
),
),
diff --git a/packages/smooth_app/lib/resources/app_icons.dart b/packages/smooth_app/lib/resources/app_icons.dart
index b046634aec6..7744c952be6 100644
--- a/packages/smooth_app/lib/resources/app_icons.dart
+++ b/packages/smooth_app/lib/resources/app_icons.dart
@@ -10,6 +10,7 @@ class Add extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add);
}
@@ -35,6 +36,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_british_pound);
@@ -42,6 +44,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_dollar);
@@ -49,6 +52,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_euro);
@@ -56,6 +60,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_ruble);
@@ -63,6 +68,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_rupee);
@@ -70,6 +76,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_swiss_franc);
@@ -77,6 +84,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_turkish_lira);
@@ -84,6 +92,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_ukrainian_hryvnia);
@@ -91,6 +100,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_won);
@@ -98,6 +108,7 @@ class AddPrice extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_price_yen);
}
@@ -108,6 +119,7 @@ class AddToList extends AppIcon {
Color? color,
double? size,
Shadow? shadow,
+ String? semanticLabel,
Key? key,
}) {
return AddToList._count(
@@ -115,6 +127,7 @@ class AddToList extends AppIcon {
color: color,
size: size,
shadow: shadow,
+ semanticLabel: semanticLabel,
key: key,
);
}
@@ -124,6 +137,7 @@ class AddToList extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(iconData);
@@ -131,6 +145,7 @@ class AddToList extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.add_to_list);
@@ -165,6 +180,7 @@ class AppStore extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.app_store);
}
@@ -174,6 +190,7 @@ class Arrow extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 0,
super._(_IconsFont.arrow_right);
@@ -182,6 +199,7 @@ class Arrow extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 2,
super._(_IconsFont.arrow_right);
@@ -190,6 +208,7 @@ class Arrow extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 1,
super._(_IconsFont.arrow_right);
@@ -198,6 +217,7 @@ class Arrow extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 3,
super._(_IconsFont.arrow_right);
@@ -218,6 +238,7 @@ class Barcode extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.barcode);
@@ -225,6 +246,7 @@ class Barcode extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.barcode_rounded);
@@ -232,6 +254,7 @@ class Barcode extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.barcode_corners);
}
@@ -241,6 +264,7 @@ class Camera extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.camera_filled);
@@ -248,6 +272,7 @@ class Camera extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.camera_outlined);
@@ -255,6 +280,7 @@ class Camera extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.camera_happy);
}
@@ -264,6 +290,7 @@ class Categories extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.categories);
}
@@ -273,6 +300,7 @@ class Chicken extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.chicken);
}
@@ -282,6 +310,7 @@ class Check extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.check);
}
@@ -291,6 +320,7 @@ class Chevron extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 2,
super._(_IconsFont.chevron_right);
@@ -299,6 +329,7 @@ class Chevron extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 0,
super._(_IconsFont.chevron_right);
@@ -307,6 +338,7 @@ class Chevron extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 2,
super._(_IconsFont.chevron_down);
@@ -315,6 +347,7 @@ class Chevron extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 0,
super._(_IconsFont.chevron_down);
@@ -335,6 +368,7 @@ class ClearText extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.clear_text);
@@ -355,6 +389,7 @@ class CircledArrow extends AppIcon {
Color? color,
double? size,
Shadow? shadow,
+ String? semanticLabel,
Key? key,
}) : this._base(
type: type,
@@ -363,6 +398,7 @@ class CircledArrow extends AppIcon {
color: color,
size: size,
shadow: shadow,
+ semanticLabel: semanticLabel,
key: key,
);
@@ -372,6 +408,7 @@ class CircledArrow extends AppIcon {
Color? color,
double? size,
Shadow? shadow,
+ String? semanticLabel,
Key? key,
}) : this._base(
type: type,
@@ -380,6 +417,7 @@ class CircledArrow extends AppIcon {
color: color,
size: size,
shadow: shadow,
+ semanticLabel: semanticLabel,
key: key,
);
@@ -389,6 +427,7 @@ class CircledArrow extends AppIcon {
Color? color,
double? size,
Shadow? shadow,
+ String? semanticLabel,
Key? key,
}) : this._base(
type: type,
@@ -397,6 +436,7 @@ class CircledArrow extends AppIcon {
color: color,
size: size,
shadow: shadow,
+ semanticLabel: semanticLabel,
key: key,
);
@@ -406,6 +446,7 @@ class CircledArrow extends AppIcon {
Color? color,
double? size,
Shadow? shadow,
+ String? semanticLabel,
Key? key,
}) : this._base(
type: type,
@@ -414,6 +455,7 @@ class CircledArrow extends AppIcon {
color: color,
size: size,
shadow: shadow,
+ semanticLabel: semanticLabel,
key: key,
);
@@ -424,12 +466,13 @@ class CircledArrow extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : assert(
(circleColor == null &&
(type == null || type == CircledArrowType.thin)) ||
(circleColor != null && type == CircledArrowType.normal),
- 'circleColor is only support and must be provided when type = CircledArrowType.normal',
+ 'circleColor is only supported and must be provided when type = CircledArrowType.normal',
),
type = type ?? CircledArrowType.thin,
super._(
@@ -478,6 +521,7 @@ class Close extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.close);
}
@@ -487,6 +531,7 @@ class Compare extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.compare);
@@ -494,6 +539,7 @@ class Compare extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.compare_disabled);
}
@@ -503,6 +549,7 @@ class Contribute extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.contribute);
}
@@ -512,6 +559,7 @@ class Countries extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.countries);
@@ -519,6 +567,7 @@ class Countries extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.country);
}
@@ -528,6 +577,7 @@ class Cupcake extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.cupcake);
}
@@ -537,6 +587,7 @@ class Crash extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.crash);
}
@@ -546,6 +597,7 @@ class Currency extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.currency);
}
@@ -555,6 +607,7 @@ class DangerousZone extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.dangerous_zone);
}
@@ -564,6 +617,7 @@ class Delete extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.delete_trash);
}
@@ -573,6 +627,7 @@ class Document extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.document);
}
@@ -582,6 +637,7 @@ class Donate extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.donate);
}
@@ -591,6 +647,7 @@ class DoubleChevron extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 3,
super._(_IconsFont.double_chevron);
@@ -599,6 +656,7 @@ class DoubleChevron extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 1,
super._(_IconsFont.double_chevron);
@@ -607,6 +665,7 @@ class DoubleChevron extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 0,
super._(_IconsFont.double_chevron);
@@ -615,6 +674,7 @@ class DoubleChevron extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 2,
super._(_IconsFont.double_chevron);
@@ -635,6 +695,7 @@ class Edit extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.edit);
}
@@ -644,6 +705,7 @@ class Environment extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.environment);
}
@@ -653,6 +715,7 @@ class ExternalLink extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.external_link);
}
@@ -662,6 +725,7 @@ class Expand extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.expand);
}
@@ -671,6 +735,7 @@ class Eye extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.eye_visible);
@@ -678,6 +743,7 @@ class Eye extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.eye_invisible);
}
@@ -687,6 +753,7 @@ class Fish extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.fish);
}
@@ -696,6 +763,7 @@ class Fruit extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.fruit);
}
@@ -705,6 +773,7 @@ class GitHub extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.github);
}
@@ -714,6 +783,7 @@ class Info extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.info);
}
@@ -723,6 +793,7 @@ class HappyToast extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.happy_toast);
}
@@ -732,6 +803,7 @@ class History extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.history);
}
@@ -741,6 +813,7 @@ class Help extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.help_circled);
}
@@ -750,6 +823,7 @@ class Incognito extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.incognito);
}
@@ -759,13 +833,23 @@ class Ingredients extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.ingredients);
+ const Ingredients.alt({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.ingredients_alt);
+
const Ingredients.basket({
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.ingredients_basket);
}
@@ -775,6 +859,7 @@ class Lab extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.lab);
}
@@ -784,6 +869,7 @@ class Labels extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.labels);
}
@@ -793,6 +879,7 @@ class Language extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.language);
@@ -800,6 +887,7 @@ class Language extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.language_world);
}
@@ -809,6 +897,7 @@ class Lifebuoy extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.lifebuoy);
}
@@ -818,15 +907,27 @@ class LightBulb extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.light_bulb);
}
+class Lists extends AppIcon {
+ const Lists({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.lists);
+}
+
class Logout extends AppIcon {
const Logout({
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.logout);
}
@@ -836,6 +937,7 @@ class MagicWand extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.magic_wand);
}
@@ -845,6 +947,7 @@ class Menu extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.hamburger_menu);
}
@@ -854,8 +957,17 @@ class Milk extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.milk);
+
+ const Milk.filled({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.milk_filled);
}
class NoPicture extends AppIcon {
@@ -863,6 +975,7 @@ class NoPicture extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.no_picture);
}
@@ -872,6 +985,7 @@ class NutritionFacts extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.nutrition_facts);
@@ -879,6 +993,7 @@ class NutritionFacts extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.nutritional_facts);
}
@@ -888,6 +1003,7 @@ class Outdated extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.outdated);
}
@@ -897,6 +1013,7 @@ class Packaging extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.packaging);
}
@@ -906,6 +1023,7 @@ class Password extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.password);
}
@@ -915,6 +1033,7 @@ class Personalization extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.personalization);
@@ -922,6 +1041,7 @@ class Personalization extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.personalization_alt);
}
@@ -931,6 +1051,7 @@ class Picture extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.image_add);
@@ -938,8 +1059,35 @@ class Picture extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.image_check);
+
+ const Picture.checkAlt({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.image_check_alt);
+
+ const Picture.error({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.image_error);
+}
+
+class Profile extends AppIcon {
+ const Profile({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.profile);
}
class Programming extends AppIcon {
@@ -947,6 +1095,7 @@ class Programming extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.programming);
}
@@ -956,6 +1105,7 @@ class Question extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.question);
@@ -963,6 +1113,7 @@ class Question extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.question_circled);
}
@@ -972,6 +1123,7 @@ class QRCode extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.qrcode);
@@ -979,15 +1131,27 @@ class QRCode extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.qrcode_corners);
}
+class Recycling extends AppIcon {
+ const Recycling({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.recycling);
+}
+
class Salt extends AppIcon {
const Salt({
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.salt);
}
@@ -997,6 +1161,7 @@ class Share extends AppIcon {
Color? color,
double? size,
Shadow? shadow,
+ String? semanticLabel,
Key? key,
}) {
if (Platform.isIOS || Platform.isMacOS) {
@@ -1004,6 +1169,7 @@ class Share extends AppIcon {
color: color,
size: size,
shadow: shadow,
+ semanticLabel: semanticLabel,
key: key,
);
} else {
@@ -1011,6 +1177,7 @@ class Share extends AppIcon {
color: color,
size: size,
shadow: shadow,
+ semanticLabel: semanticLabel,
key: key,
);
}
@@ -1020,6 +1187,7 @@ class Share extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.share_material);
@@ -1027,6 +1195,7 @@ class Share extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.share_cupertino);
}
@@ -1036,22 +1205,43 @@ class Search extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.search);
+ const Search.alt({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.search_alt);
+
const Search.advanced({
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.advanced_search);
}
+class Select extends AppIcon {
+ const Select.photo({
+ super.color,
+ super.size,
+ super.shadow,
+ super.semanticLabel,
+ super.key,
+ }) : super._(_IconsFont.photo_select);
+}
+
class Settings extends AppIcon {
const Settings({
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.settings);
}
@@ -1061,6 +1251,7 @@ class Soda extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.soda_happy);
@@ -1068,6 +1259,7 @@ class Soda extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.soda_unhappy);
}
@@ -1077,6 +1269,7 @@ class Sound extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.sound_on);
@@ -1084,6 +1277,7 @@ class Sound extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.sound_off);
}
@@ -1093,6 +1287,7 @@ class Sparkles extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.sparkles);
}
@@ -1102,6 +1297,7 @@ class Stores extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.store);
}
@@ -1111,6 +1307,7 @@ class Suggestion extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.suggestion);
}
@@ -1120,6 +1317,7 @@ class ThreeDots extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 0,
super._(_IconsFont.dots_vertical);
@@ -1128,6 +1326,7 @@ class ThreeDots extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : turns = 1,
super._(_IconsFont.dots_vertical);
@@ -1148,6 +1347,7 @@ class ToggleCamera extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.toggle_camera);
}
@@ -1157,6 +1357,7 @@ class Torch extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.torch_on);
@@ -1164,6 +1365,7 @@ class Torch extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.torch_off);
}
@@ -1173,6 +1375,7 @@ class Vibration extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.vibration);
}
@@ -1182,6 +1385,7 @@ class Warning extends AppIcon {
super.color,
super.size,
super.shadow,
+ super.semanticLabel,
super.key,
}) : super._(_IconsFont.warning);
}
diff --git a/packages/smooth_app/lib/resources/app_icons_font.dart b/packages/smooth_app/lib/resources/app_icons_font.dart
index 2c32d2b32ff..850e8c73836 100644
--- a/packages/smooth_app/lib/resources/app_icons_font.dart
+++ b/packages/smooth_app/lib/resources/app_icons_font.dart
@@ -201,6 +201,8 @@ class _IconsFont {
IconData(0xe85e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData logout =
IconData(0xe85f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData lists =
+ IconData(0xe860, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData donate =
IconData(0xe861, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData dangerous_zone =
@@ -227,8 +229,24 @@ class _IconsFont {
IconData(0xe86d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData add_to_list_9 =
IconData(0xe86e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData search_alt =
+ IconData(0xe86f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData profile =
+ IconData(0xe870, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData photo_select =
+ IconData(0xe871, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData image_check_alt =
+ IconData(0xe875, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData image_error =
+ IconData(0xe876, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData share_cupertino =
IconData(0xe8a4, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData share_material =
IconData(0xe8a5, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData ingredients_alt =
+ IconData(0xe8e0, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData milk_filled =
+ IconData(0xe8e1, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+ static const IconData recycling =
+ IconData(0xe8e2, fontFamily: _kFontFam, fontPackage: _kFontPkg);
}
diff --git a/packages/smooth_app/lib/themes/smooth_theme.dart b/packages/smooth_app/lib/themes/smooth_theme.dart
index 99dca596949..70bb8064305 100644
--- a/packages/smooth_app/lib/themes/smooth_theme.dart
+++ b/packages/smooth_app/lib/themes/smooth_theme.dart
@@ -88,6 +88,7 @@ class SmoothTheme {
foregroundColor: myColorScheme.onPrimary),
textTheme: textTheme,
appBarTheme: AppBarTheme(
+ centerTitle: false,
color: myColorScheme.surface,
foregroundColor: myColorScheme.onSurface,
systemOverlayStyle: SystemUiOverlayStyle.light,
diff --git a/packages/smooth_app/lib/widgets/smooth_app_bar.dart b/packages/smooth_app/lib/widgets/smooth_app_bar.dart
index 74091362222..68a2d61157f 100644
--- a/packages/smooth_app/lib/widgets/smooth_app_bar.dart
+++ b/packages/smooth_app/lib/widgets/smooth_app_bar.dart
@@ -298,7 +298,8 @@ class _AppBarTitle extends StatelessWidget {
),
if (subTitle != null)
DefaultTextStyle(
- style: theme.textTheme.bodyMedium ?? const TextStyle(),
+ style: (theme.textTheme.bodyMedium ?? const TextStyle())
+ .copyWith(color: color),
child: ExcludeSemantics(
excluding: ignoreSemanticsForSubtitle ?? false,
child: subTitle,
diff --git a/packages/smooth_app/lib/widgets/smooth_barcode_widget.dart b/packages/smooth_app/lib/widgets/smooth_barcode_widget.dart
index fd09b41a847..f10b3931714 100644
--- a/packages/smooth_app/lib/widgets/smooth_barcode_widget.dart
+++ b/packages/smooth_app/lib/widgets/smooth_barcode_widget.dart
@@ -43,7 +43,7 @@ class SmoothBarcodeWidget extends StatelessWidget {
padding: EdgeInsets.zero,
data: barcode,
barcode: _barcodeType,
- color: Colors.black,
+ color: color ?? Colors.black,
style: TextStyle(
color: contentColor,
),
diff --git a/packages/smooth_app/lib/widgets/smooth_tabbar.dart b/packages/smooth_app/lib/widgets/smooth_tabbar.dart
index 5b35663c355..c6cfdb04658 100644
--- a/packages/smooth_app/lib/widgets/smooth_tabbar.dart
+++ b/packages/smooth_app/lib/widgets/smooth_tabbar.dart
@@ -41,7 +41,8 @@ class _SmoothTabBarState extends State> {
painter: _ProductHeaderTabBarPainter(
progress: _horizontalProgress,
primaryColor: lightTheme ? theme.primaryLight : theme.primaryDark,
- bottomSeparatorColor: theme.primaryDark,
+ bottomSeparatorColor:
+ lightTheme ? theme.primaryBlack : theme.primaryNormal,
backgroundColor: AppBarTheme.of(context).backgroundColor ??
Theme.of(context).scaffoldBackgroundColor,
),
@@ -73,7 +74,9 @@ class _SmoothTabBarState extends State> {
labelPadding: EdgeInsets.zero,
tabAlignment: TabAlignment.start,
overlayColor: WidgetStatePropertyAll(
- theme.primaryLight,
+ lightTheme
+ ? theme.primaryNormal.withOpacity(0.2)
+ : theme.primaryLight.withOpacity(0.2),
),
splashBorderRadius: const BorderRadius.vertical(
top: Radius.circular(5.0),
@@ -86,8 +89,9 @@ class _SmoothTabBarState extends State> {
unselectedLabelStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15.0,
- color: lightTheme ? theme.primarySemiDark : theme.primaryNormal,
+ color: lightTheme ? theme.primaryBlack : theme.primaryMedium,
),
+ dividerColor: theme.primaryDark,
indicator: BoxDecoration(
border: Border(
bottom: BorderSide(