├── 10 ├── analysis_options.yaml ├── format_check.bat ├── format_check.sh ├── lib │ ├── main.dart │ ├── pages │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── home_page │ │ │ └── index.dart │ │ └── user_page │ │ │ └── index.dart │ ├── project_router.dart │ └── util │ │ └── tools │ │ └── json_config.dart ├── pubspec.yaml └── test │ └── pages │ ├── home_page │ └── index_test.dart │ └── user_page │ └── index_test.dart ├── 11 ├── analysis_options.yaml ├── format_check.bat ├── format_check.sh ├── lib │ ├── main.dart │ ├── pages │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance_bottom_bar.dart │ │ ├── entrance_top_bar.dart │ │ ├── home_page │ │ │ └── index.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ └── user_page │ │ │ └── index.dart │ ├── project_router.dart │ ├── util │ │ ├── struct │ │ │ └── router_struct.dart │ │ └── tools │ │ │ └── json_config.dart │ └── widgets │ │ └── menu │ │ └── draw.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_bottom_bar_test.dart │ ├── entrance_top_bar_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 12 ├── analysis_options.yaml ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ └── user_info │ │ │ └── index.dart │ ├── main.dart │ ├── model │ │ └── like_num_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ └── user_page │ │ │ └── index.dart │ ├── project_router.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── router.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ └── json_config.dart │ └── widgets │ │ ├── article_detail │ │ ├── article_comments.dart │ │ ├── article_content.dart │ │ └── article_detail_like.dart │ │ ├── common │ │ └── banner_info.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_card.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ └── menu │ │ └── draw.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 13 ├── analysis_options.yaml ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ └── user_info │ │ │ └── index.dart │ ├── main.dart │ ├── model │ │ └── like_num_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ └── user_page │ │ │ └── index.dart │ ├── project_router.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── api_ret_info.dart │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── router.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ └── json_config.dart │ └── widgets │ │ ├── article_detail │ │ ├── article_comments.dart │ │ ├── article_content.dart │ │ └── article_detail_like.dart │ │ ├── common │ │ ├── banner_info.dart │ │ ├── error.dart │ │ └── loading.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_card.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ └── menu │ │ └── draw.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 14 ├── README.md ├── analysis_options.yaml ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ └── user_info │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── main.dart │ ├── model │ │ ├── like_num_model.dart │ │ ├── new_message_model.dart │ │ └── user_info_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ └── user_page │ │ │ └── index.dart │ ├── project_router.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── api_ret_info.dart │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── router.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ └── json_config.dart │ └── widgets │ │ ├── article_detail │ │ ├── article_comments.dart │ │ ├── article_content.dart │ │ └── article_detail_like.dart │ │ ├── common │ │ ├── banner_info.dart │ │ ├── error.dart │ │ ├── loading.dart │ │ └── red_message.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_card.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ ├── menu │ │ └── draw.dart │ │ └── user_page │ │ ├── button_list.dart │ │ └── header.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 15 ├── README.md ├── analysis_options.yaml ├── assets │ └── json │ │ └── api.json ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ └── user_info │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── api_pb │ │ └── user_info │ │ │ └── index.dart │ ├── api_xml │ │ └── user_info │ │ │ └── index.dart │ ├── main.dart │ ├── model │ │ ├── like_num_model.dart │ │ ├── new_message_model.dart │ │ └── user_info_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ └── user_page │ │ │ └── index.dart │ ├── project_router.dart │ ├── protos │ │ ├── user_info.pb.dart │ │ ├── user_info.pbenum.dart │ │ ├── user_info.pbjson.dart │ │ └── user_info.pbserver.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── api_ret_info.dart │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── router.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ ├── call_server.dart │ │ │ └── json_config.dart │ └── widgets │ │ ├── article_detail │ │ ├── article_comments.dart │ │ ├── article_content.dart │ │ └── article_detail_like.dart │ │ ├── common │ │ ├── banner_info.dart │ │ ├── error.dart │ │ ├── loading.dart │ │ └── red_message.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_card.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ ├── menu │ │ └── draw.dart │ │ └── user_page │ │ ├── button_list.dart │ │ └── header.dart ├── protos │ └── user_info.proto ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 16 ├── README.md ├── analysis_options.yaml ├── assets │ └── json │ │ └── api.json ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ └── user_info │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── main.dart │ ├── model │ │ ├── like_num_model.dart │ │ ├── new_message_model.dart │ │ └── user_info_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── follow_page │ │ │ └── index.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ └── user_page │ │ │ ├── guest.dart │ │ │ └── index.dart │ ├── project_router.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── api_ret_info.dart │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── router.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ ├── call_server.dart │ │ │ └── json_config.dart │ └── widgets │ │ ├── article_detail │ │ ├── comments.dart │ │ ├── content.dart │ │ ├── img.dart │ │ ├── like.dart │ │ ├── title.dart │ │ └── user_info_bar.dart │ │ ├── common │ │ ├── article_card.dart │ │ ├── banner_info.dart │ │ ├── error.dart │ │ ├── loading.dart │ │ └── red_message.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ ├── menu │ │ └── draw.dart │ │ └── user_page │ │ ├── button_list.dart │ │ ├── content_list.dart │ │ ├── guest_bar.dart │ │ ├── guest_header.dart │ │ └── header.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 18 ├── README.md ├── analysis_options.yaml ├── assets │ └── json │ │ └── api.json ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ ├── search │ │ │ └── index.dart │ │ └── user_info │ │ │ ├── follow.dart │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── main.dart │ ├── model │ │ ├── like_num_model.dart │ │ ├── new_message_model.dart │ │ ├── system_config_model.dart │ │ └── user_info_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── follow_page │ │ │ ├── index.dart │ │ │ └── list.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ ├── system_config_page │ │ │ └── index.dart │ │ └── user_page │ │ │ ├── guest.dart │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── project_router.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── api_ret_info.dart │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── message.dart │ │ │ ├── router.dart │ │ │ ├── system_config.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ ├── call_server.dart │ │ │ ├── json_config.dart │ │ │ ├── local_storage.dart │ │ │ └── time_format.dart │ └── widgets │ │ ├── article_detail │ │ ├── comments.dart │ │ ├── content.dart │ │ ├── img.dart │ │ ├── like.dart │ │ ├── title.dart │ │ └── user_info_bar.dart │ │ ├── common │ │ ├── article_card.dart │ │ ├── banner_info.dart │ │ ├── error.dart │ │ ├── loading.dart │ │ └── red_message.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ ├── menu │ │ └── draw.dart │ │ ├── search │ │ └── content_card.dart │ │ ├── system_page │ │ └── switch_card.dart │ │ └── user_page │ │ ├── button_list.dart │ │ ├── card.dart │ │ ├── content_list.dart │ │ ├── guest_bar.dart │ │ ├── guest_header.dart │ │ ├── header.dart │ │ └── message_card.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 19 ├── README.md ├── analysis_options.yaml ├── assets │ └── json │ │ └── api.json ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ ├── search │ │ │ └── index.dart │ │ └── user_info │ │ │ ├── follow.dart │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── main.dart │ ├── model │ │ ├── like_num_model.dart │ │ ├── new_message_model.dart │ │ ├── system_config_model.dart │ │ └── user_info_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── follow_page │ │ │ ├── index.dart │ │ │ └── list.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ ├── system_config_page │ │ │ └── index.dart │ │ └── user_page │ │ │ ├── guest.dart │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── project_router.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── api_ret_info.dart │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── message.dart │ │ │ ├── router.dart │ │ │ ├── system_config.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ ├── app_provider.dart │ │ │ ├── app_sentry.dart │ │ │ ├── call_server.dart │ │ │ ├── json_config.dart │ │ │ ├── local_storage.dart │ │ │ └── time_format.dart │ └── widgets │ │ ├── article_detail │ │ ├── comments.dart │ │ ├── content.dart │ │ ├── img.dart │ │ ├── like.dart │ │ ├── title.dart │ │ └── user_info_bar.dart │ │ ├── common │ │ ├── article_card.dart │ │ ├── banner_info.dart │ │ ├── error.dart │ │ ├── loading.dart │ │ └── red_message.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ ├── menu │ │ └── draw.dart │ │ ├── search │ │ └── content_card.dart │ │ ├── system_page │ │ └── switch_card.dart │ │ └── user_page │ │ ├── button_list.dart │ │ ├── card.dart │ │ ├── content_list.dart │ │ ├── guest_bar.dart │ │ ├── guest_header.dart │ │ ├── header.dart │ │ └── message_card.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 20 ├── README.md ├── analysis_options.yaml ├── assets │ └── json │ │ └── api.json ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ ├── search │ │ │ └── index.dart │ │ └── user_info │ │ │ ├── follow.dart │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── main.dart │ ├── model │ │ ├── like_num_model.dart │ │ ├── new_message_model.dart │ │ ├── system_config_model.dart │ │ └── user_info_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── follow_page │ │ │ ├── index.dart │ │ │ └── list.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ ├── system_config_page │ │ │ └── index.dart │ │ ├── test_page │ │ │ └── index.dart │ │ └── user_page │ │ │ ├── guest.dart │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── project_router.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── api_ret_info.dart │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── message.dart │ │ │ ├── router.dart │ │ │ ├── system_config.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ ├── app_provider.dart │ │ │ ├── app_sentry.dart │ │ │ ├── call_server.dart │ │ │ ├── json_config.dart │ │ │ ├── local_storage.dart │ │ │ └── time_format.dart │ └── widgets │ │ ├── article_detail │ │ ├── comments.dart │ │ ├── content.dart │ │ ├── img.dart │ │ ├── like.dart │ │ ├── title.dart │ │ └── user_info_bar.dart │ │ ├── common │ │ ├── article_card.dart │ │ ├── banner_info.dart │ │ ├── error.dart │ │ ├── loading.dart │ │ └── red_message.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ ├── menu │ │ └── draw.dart │ │ ├── search │ │ └── content_card.dart │ │ ├── system_page │ │ └── switch_card.dart │ │ └── user_page │ │ ├── button_list.dart │ │ ├── card.dart │ │ ├── content_list.dart │ │ ├── guest_bar.dart │ │ ├── guest_header.dart │ │ ├── header.dart │ │ └── message_card.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── 21 ├── README.md ├── analysis_options.yaml ├── assets │ └── json │ │ └── api.json ├── format_check.bat ├── format_check.sh ├── lib │ ├── api │ │ ├── content │ │ │ ├── comment.dart │ │ │ └── index.dart │ │ ├── search │ │ │ └── index.dart │ │ └── user_info │ │ │ ├── follow.dart │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── main.dart │ ├── model │ │ ├── like_num_model.dart │ │ ├── new_message_model.dart │ │ ├── system_config_model.dart │ │ └── user_info_model.dart │ ├── pages │ │ ├── article_detail │ │ │ └── index.dart │ │ ├── common │ │ │ └── web_view_page.dart │ │ ├── entrance.dart │ │ ├── follow_page │ │ │ ├── index.dart │ │ │ └── list.dart │ │ ├── home_page │ │ │ ├── img_flow.dart │ │ │ ├── index.dart │ │ │ └── single.dart │ │ ├── search_page │ │ │ └── custom_delegate.dart │ │ ├── system_config_page │ │ │ └── index.dart │ │ ├── test_page │ │ │ └── index.dart │ │ └── user_page │ │ │ ├── guest.dart │ │ │ ├── index.dart │ │ │ └── message.dart │ ├── project_router.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ ├── struct │ │ │ ├── api_ret_info.dart │ │ │ ├── comment_info.dart │ │ │ ├── content_detail.dart │ │ │ ├── message.dart │ │ │ ├── router.dart │ │ │ ├── system_config.dart │ │ │ └── user_info.dart │ │ └── tools │ │ │ ├── app_provider.dart │ │ │ ├── app_sentry.dart │ │ │ ├── call_server.dart │ │ │ ├── islolate_handle.dart │ │ │ ├── json_config.dart │ │ │ ├── local_storage.dart │ │ │ ├── report.dart │ │ │ └── time_format.dart │ └── widgets │ │ ├── article_detail │ │ ├── comments.dart │ │ ├── content.dart │ │ ├── img.dart │ │ ├── like.dart │ │ ├── title.dart │ │ └── user_info_bar.dart │ │ ├── common │ │ ├── article_card.dart │ │ ├── banner_info.dart │ │ ├── error.dart │ │ ├── loading.dart │ │ └── red_message.dart │ │ ├── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_like_bar.dart │ │ ├── article_summary.dart │ │ ├── img_card.dart │ │ ├── single_bottom_summary.dart │ │ ├── single_like_bar.dart │ │ └── single_right_bar.dart │ │ ├── menu │ │ └── draw.dart │ │ ├── search │ │ └── content_card.dart │ │ ├── system_page │ │ └── switch_card.dart │ │ └── user_page │ │ ├── button_list.dart │ │ ├── card.dart │ │ ├── content_list.dart │ │ ├── guest_bar.dart │ │ ├── guest_header.dart │ │ ├── header.dart │ │ └── message_card.dart ├── pubspec.yaml └── test │ ├── pages │ ├── common │ │ └── web_view_page_test.dart │ ├── entrance_test.dart │ ├── home_page │ │ └── index_test.dart │ └── user_page │ │ └── index_test.dart │ └── util │ └── struct │ └── router_struct_test.dart ├── .gitignore ├── 02 ├── block_task.dart ├── event_with_microtask.dart ├── flow.dart ├── isolate.dart └── overall.dart ├── 03 ├── README.md ├── analysis_options.yaml ├── format_check.sh ├── lib │ └── main.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── 04 ├── README.md ├── analysis_options.yaml ├── format_check.sh ├── lib │ ├── main.dart │ └── pages │ │ └── home_page.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── 05 ├── first_rebuild │ ├── README.md │ ├── analysis_options.yaml │ ├── format_check.sh │ ├── lib │ │ ├── main.dart │ │ └── pages │ │ │ └── test_stateful_widget.dart │ ├── pubspec.yaml │ └── test │ │ └── widget_test.dart ├── overall │ ├── README.md │ ├── analysis_options.yaml │ ├── format_check.sh │ ├── lib │ │ ├── main.dart │ │ └── pages │ │ │ └── home_page.dart │ ├── pubspec.yaml │ └── test │ │ └── widget_test.dart └── with_sub_widget-rebuild │ ├── README.md │ ├── analysis_options.yaml │ ├── format_check.sh │ ├── lib │ ├── main.dart │ └── pages │ │ ├── home_page.dart │ │ ├── sub_stateful_widget.dart │ │ └── test_stateful_widget.dart │ ├── pubspec.yaml │ └── test │ └── widget_test.dart ├── 06 ├── README.md ├── analysis_options.yaml ├── format_check.sh ├── lib │ ├── main.dart │ ├── pages │ │ └── home_page.dart │ ├── util │ │ └── struct │ │ │ ├── article_summary_struct.dart │ │ │ └── user_info_struct.dart │ └── widgets │ │ ├── common │ │ └── banner_info.dart │ │ └── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_card.dart │ │ ├── article_like_bar.dart │ │ └── article_summary.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── 07 ├── InheritedWidget │ ├── README.md │ ├── analysis_options.yaml │ ├── format_check.sh │ ├── lib │ │ ├── inherited_widget │ │ │ └── name_inherited_widget.dart │ │ ├── main.dart │ │ ├── pages │ │ │ └── name_game.dart │ │ └── widgets │ │ │ └── name_game │ │ │ ├── random_name.dart │ │ │ ├── test_other.dart │ │ │ └── welcome.dart │ ├── pubspec.yaml │ └── test │ │ └── widget_test.dart ├── Overall │ ├── README.md │ ├── analysis_options.yaml │ ├── format_check.sh │ ├── lib │ │ ├── main.dart │ │ ├── model │ │ │ ├── like_num_model.dart │ │ │ └── name_model.dart │ │ ├── pages │ │ │ ├── article_detail.dart │ │ │ └── home_page.dart │ │ ├── styles │ │ │ └── text_syles.dart │ │ ├── util │ │ │ └── struct │ │ │ │ ├── article_summary_struct.dart │ │ │ │ ├── comment_info_struct.dart │ │ │ │ └── user_info_struct.dart │ │ └── widgets │ │ │ ├── article_detail │ │ │ ├── article_comments.dart │ │ │ ├── article_content.dart │ │ │ └── article_detail_like.dart │ │ │ ├── common │ │ │ └── banner_info.dart │ │ │ └── home_page │ │ │ ├── article_bottom_bar.dart │ │ │ ├── article_card.dart │ │ │ ├── article_like_bar.dart │ │ │ └── article_summary.dart │ └── pubspec.yaml ├── Provider │ ├── README.md │ ├── analysis_options.yaml │ ├── format_check.sh │ ├── lib │ │ ├── main.dart │ │ ├── model │ │ │ └── name_model.dart │ │ ├── pages │ │ │ └── name_game.dart │ │ └── widgets │ │ │ └── name_game │ │ │ ├── random_name.dart │ │ │ ├── test_other.dart │ │ │ └── welcome.dart │ └── pubspec.yaml └── Redux │ ├── README.md │ ├── analysis_options.yaml │ ├── format_check.sh │ ├── lib │ ├── main.dart │ ├── pages │ │ └── name_game.dart │ ├── states │ │ └── name_states.dart │ └── widgets │ │ └── name_game │ │ ├── random_name.dart │ │ ├── test_other.dart │ │ └── welcome.dart │ └── pubspec.yaml ├── 08 ├── README.md ├── analysis_options.yaml ├── build │ └── testfile.dill.track.dill ├── format_check.sh ├── lib │ ├── main.dart │ ├── model │ │ └── like_num_model.dart │ ├── pages │ │ ├── article_detail.dart │ │ └── home_page.dart │ ├── styles │ │ └── text_syles.dart │ ├── util │ │ └── struct │ │ │ ├── article_summary_struct.dart │ │ │ ├── comment_info_struct.dart │ │ │ └── user_info_struct.dart │ └── widgets │ │ ├── article_detail │ │ ├── article_comments.dart │ │ ├── article_content.dart │ │ └── article_detail_like.dart │ │ ├── common │ │ └── banner_info.dart │ │ └── home_page │ │ ├── article_bottom_bar.dart │ │ ├── article_card.dart │ │ ├── article_like_bar.dart │ │ └── article_summary.dart ├── pubspec.yaml └── test │ ├── model │ └── like_num_model_test.dart │ ├── util │ └── struct │ │ ├── article_summary_struct_test.dart │ │ ├── comment_info_struct_test.dart │ │ └── user_info_struct_test.dart │ └── widgets │ ├── article_detail │ ├── article_comments_test.dart │ ├── article_content_test.dart │ └── article_detail_like_test.dart │ ├── common │ └── banner_info_test.dart │ └── home_page │ ├── article_bottom_bar_test.dart │ ├── article_card_test.dart │ ├── article_like_bar_test.dart │ └── article_summary_test.dart └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .dart_tool 2 | .DS_Store 3 | .flutter-plugins-dependencies 4 | .packages 5 | pubspec.lock 6 | .flutter-plugins -------------------------------------------------------------------------------- /02/block_task.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | void main() { 4 | print('flow start'); // 执行打印开始 5 | 6 | // 执行判断为事件任务,添加到事件任务队列 7 | Timer.run((){ 8 | for(int i=0; i<1000000000; i++){ // 大循环,为了卡住事件任务执行时间,检查是否会卡住其他任务执行 9 | if(i == 1000000){ 10 | 11 | // 执行判断为微任务,添加到微任务队列 12 | scheduleMicrotask((){ 13 | print('microtask in event'); // 执行微任务,打印微任务标记 14 | }); 15 | } 16 | } 17 | print('event'); // 执行完事件任务,打印执行完事件任务标记 18 | }); 19 | 20 | // 执行判断为微任务,添加到微任务队列 21 | scheduleMicrotask((){ 22 | print('microtask'); // 执行微任务,打印微任务标记 23 | 24 | // 执行判断为事件任务,添加到事件任务队列 25 | Timer.run((){ 26 | print('event in microtask'); // 执行事件任务,打印事件任务标记 27 | }); 28 | }); 29 | 30 | print('flow end'); // 打印结束标记 31 | } -------------------------------------------------------------------------------- /02/event_with_microtask.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | void main() { 4 | print('flow start'); // 执行打印开始 5 | 6 | // 执行判断为事件任务,添加到事件任务队列 7 | Timer.run((){ 8 | print('event'); // 执行事件任务,打印事件任务标记 9 | 10 | // 执行判断为微任务,添加到微任务队列 11 | scheduleMicrotask((){ 12 | print('microtask in event'); // 执行微任务,打印微任务标记 13 | }); 14 | }); 15 | 16 | // 执行判断为微任务,添加到微任务队列 17 | scheduleMicrotask((){ 18 | print('microtask'); // 执行微任务,打印微任务执行标记 19 | 20 | // 执行判断为事件任务,添加到事件任务队列 21 | Timer.run((){ 22 | print('event in microtask'); // 执行事件任务,打印事件任务标记 23 | }); 24 | }); 25 | 26 | print('flow end'); // 打印结束标记 27 | } -------------------------------------------------------------------------------- /02/flow.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | void main() { 4 | print('flow start'); // 执行打印开始 5 | 6 | // 执行判断为事件任务,添加到事件任务队列 7 | Timer.run((){ 8 | print('event'); // 执行事件任务,打印标记 9 | }); 10 | 11 | // 执行判断为微任务,添加到微任务队列 12 | scheduleMicrotask((){ 13 | print('microtask'); // 执行微任务,打印标记 14 | }); 15 | 16 | print('flow end'); // 打印结束标记 17 | } -------------------------------------------------------------------------------- /02/isolate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:isolate'; 3 | 4 | Isolate isolate; 5 | 6 | String name = 'dart'; 7 | 8 | void main() { 9 | // 执行新线程创建函数 10 | isolateServer(); 11 | } 12 | 13 | /// 多线程函数 14 | void isolateServer()async{ 15 | // 创建新的线程,并且执行回调 changName 16 | final receive = ReceivePort(); 17 | isolate = await Isolate.spawn(changName, receive.sendPort); 18 | 19 | // 监听线程返回信息 20 | receive.listen((data){ 21 | print("Myname is $data"); // 打印线程返回的数据 22 | print("Myname is $name"); // 打印全局 name 的数据 23 | }); 24 | } 25 | 26 | /// 线程回调处理函数 27 | void changName(SendPort port){ 28 | name = 'dart isloate'; // 修改当前全局 name 属性 29 | port.send(name); // 将当前name发送给监听方 30 | print("Myname is $name in isloate"); // 打印当前线程中的 name 31 | } -------------------------------------------------------------------------------- /03/README.md: -------------------------------------------------------------------------------- 1 | ### 03课时源码 -------------------------------------------------------------------------------- /03/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /03/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /03/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void main() => runApp(MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | // This widget is the root of your application. 7 | @override 8 | Widget build(BuildContext context) { 9 | return MaterialApp( 10 | title: 'Two You', // app 的title信息 11 | theme: ThemeData( 12 | primarySwatch: Colors.blue, // 页面的主题颜色 13 | ), 14 | home: Scaffold( 15 | appBar: AppBar( 16 | title: Text('Two You'), // 当前页面的 title 信息 17 | ), 18 | body: Center( 19 | child: Text('Hello Flutter'), // 当前页面的显示的文本信息 20 | ) 21 | ) 22 | ); 23 | } 24 | } -------------------------------------------------------------------------------- /04/README.md: -------------------------------------------------------------------------------- 1 | ### 04课时代码 -------------------------------------------------------------------------------- /04/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /04/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /04/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:two_you_friend/pages/home_page.dart'; 3 | 4 | /// APP 核心入口文件 5 | void main() => runApp(MyApp()); 6 | 7 | /// MyApp 核心入口界面 8 | class MyApp extends StatelessWidget { 9 | // This widget is the root of your application. 10 | @override 11 | Widget build(BuildContext context) { 12 | return MaterialApp( 13 | title: 'Two You', 14 | theme: ThemeData( 15 | primarySwatch: Colors.blue, 16 | ), 17 | home: Scaffold( 18 | appBar: AppBar( 19 | title: Text('Two You'), 20 | ), 21 | body: Center( 22 | child: HomePage(), 23 | ))); 24 | } 25 | } -------------------------------------------------------------------------------- /04/lib/pages/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:intl/intl.dart'; // 需要在pubspec.yaml增加该模块 3 | 4 | 5 | /// APP 首页入口 6 | /// 7 | /// 本模块函数,加载状态类组件HomePageState 8 | class HomePage extends StatefulWidget { 9 | @override 10 | createState() => HomePageState(); 11 | } 12 | 13 | /// 首页有状态组件类 14 | /// 15 | /// 主要是获取当前时间,并动态展示当前时间 16 | class HomePageState extends State { 17 | /// 获取当前时间戳 18 | /// 19 | /// [prefix]需要传入一个前缀信息 20 | /// 返回一个字符串类型的前缀信息:时间戳 21 | String getCurrentTime(String prefix) { 22 | DateTime now = DateTime.now(); 23 | var formatter = DateFormat('yy-mm-dd H:m:s'); 24 | String nowTime = formatter.format(now); 25 | 26 | return '$prefix $nowTime'; 27 | } 28 | 29 | /// 有状态类返回组件信息 30 | @override 31 | Widget build(BuildContext context) { 32 | return Text( 33 | getCurrentTime('当前时间') 34 | ); 35 | } 36 | } -------------------------------------------------------------------------------- /05/first_rebuild/README.md: -------------------------------------------------------------------------------- 1 | ### 组件首次加载执行过程 -------------------------------------------------------------------------------- /05/first_rebuild/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /05/first_rebuild/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /05/first_rebuild/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:two_you_friend/pages/test_stateful_widget.dart'; 3 | 4 | /// APP 核心入口文件 5 | void main() => runApp(MyApp()); 6 | 7 | /// MyApp 核心入口界面 8 | class MyApp extends StatelessWidget { 9 | // This widget is the root of your application. 10 | @override 11 | Widget build(BuildContext context) { 12 | return MaterialApp( 13 | title: 'Two You', // APP 名字 14 | debugShowCheckedModeBanner: false, 15 | theme: ThemeData( 16 | primarySwatch: Colors.blue, // APP 主题 17 | ), 18 | home: Scaffold( 19 | appBar: AppBar( 20 | title: Text('Two You'), // 页面名字 21 | ), 22 | body: Center( 23 | child: 24 | TestStatefulWidget(), 25 | ) 26 | )); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /05/overall/README.md: -------------------------------------------------------------------------------- 1 | ### 组件首次加载执行过程 -------------------------------------------------------------------------------- /05/overall/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /05/overall/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /05/overall/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:two_you_friend/pages/home_page.dart'; 3 | 4 | /// APP 核心入口文件 5 | void main() => runApp(MyApp()); 6 | 7 | /// MyApp 核心入口界面 8 | class MyApp extends StatelessWidget { 9 | // This widget is the root of your application. 10 | @override 11 | Widget build(BuildContext context) { 12 | return MaterialApp( 13 | title: 'Two You', // APP 名字 14 | theme: ThemeData( 15 | primarySwatch: Colors.blue, // APP 主题 16 | ), 17 | home: Scaffold( 18 | appBar: AppBar( 19 | title: Text('Two You'), // 页面名字 20 | ), 21 | body: Center( 22 | child: HomePage(), // 加载 HomePage 组件 23 | ))); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /05/with_sub_widget-rebuild/README.md: -------------------------------------------------------------------------------- 1 | ### 组件首次加载执行过程 -------------------------------------------------------------------------------- /05/with_sub_widget-rebuild/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /05/with_sub_widget-rebuild/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /05/with_sub_widget-rebuild/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:two_you_friend/pages/test_stateful_widget.dart'; 3 | 4 | /// APP 核心入口文件 5 | void main() => runApp(MyApp()); 6 | 7 | /// MyApp 核心入口界面 8 | class MyApp extends StatelessWidget { 9 | // This widget is the root of your application. 10 | @override 11 | Widget build(BuildContext context) { 12 | return MaterialApp( 13 | title: 'Two You', // APP 名字 14 | debugShowCheckedModeBanner: false, 15 | theme: ThemeData( 16 | primarySwatch: Colors.blue, // APP 主题 17 | ), 18 | home: Scaffold( 19 | appBar: AppBar( 20 | title: Text('Two You'), // 页面名字 21 | ), 22 | body: Center( 23 | child: TestStatefulWidget(), 24 | ))); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /06/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /06/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /06/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /06/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/pages/home_page.dart'; 4 | 5 | /// APP 核心入口文件 6 | void main() => runApp(MyApp()); 7 | 8 | /// MyApp 核心入口界面 9 | class MyApp extends StatelessWidget { 10 | // This widget is the root of your application. 11 | @override 12 | Widget build(BuildContext context) { 13 | return MaterialApp( 14 | title: 'Two You', // APP 名字 15 | debugShowCheckedModeBanner: false, 16 | theme: ThemeData( 17 | primarySwatch: Colors.blue, // APP 主题 18 | ), 19 | home: Scaffold( 20 | appBar: AppBar( 21 | title: Text('Two You'), // 页面名字 22 | ), 23 | body: Center( 24 | child: HomePage(), 25 | ))); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /06/lib/util/struct/article_summary_struct.dart: -------------------------------------------------------------------------------- 1 | /// 描述帖子的数据结构 2 | class ArticleSummaryStruct { 3 | /// 帖子标题 4 | final String title; 5 | 6 | /// 帖子概要 7 | final String summary; 8 | 9 | /// 帖子一张图 10 | final String articleImage; 11 | 12 | /// 点赞数量 13 | final int likeNum; 14 | 15 | /// 评论数量 16 | final int commentNum; 17 | 18 | /// 构造函数 19 | const ArticleSummaryStruct(this.title, this.summary, this.articleImage, 20 | this.likeNum, this.commentNum); 21 | } 22 | -------------------------------------------------------------------------------- /06/lib/util/struct/user_info_struct.dart: -------------------------------------------------------------------------------- 1 | /// userInfo数据结构描述信息 2 | class UserInfoStruct { 3 | /// 用户的昵称 4 | final String nickname; 5 | 6 | /// 用户头像信息 7 | final String headerImage; 8 | 9 | /// 构造函数 10 | const UserInfoStruct(this.nickname, this.headerImage); 11 | } 12 | -------------------------------------------------------------------------------- /06/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 帖子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | fit: BoxFit.cover, 28 | ), 29 | ], 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /06/lib/widgets/home_page/article_summary.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 帖子概要信息 4 | /// 5 | /// 包括帖子的标题,帖子描述和帖子中的图片 6 | class ArticleSummary extends StatelessWidget { 7 | /// 帖子标题 8 | final String title; 9 | 10 | /// 帖子概要描述信息 11 | final String summary; 12 | 13 | /// 帖子中的图片信息 14 | final String articleImage; 15 | 16 | /// 构造函数 17 | const ArticleSummary({Key key, this.title, this.summary, this.articleImage}) 18 | : super(key: key); 19 | 20 | /// 左侧的标题和标题描述组件 21 | Widget getLeftInfo() { 22 | return Column( 23 | children: [Text(title), Text(summary)], 24 | ); 25 | } 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Row( 30 | children: [ 31 | getLeftInfo(), 32 | Image.network( 33 | articleImage, 34 | width: 80.0, 35 | height: 80.0, 36 | fit: BoxFit.cover, 37 | ), 38 | ], 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /07/InheritedWidget/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /07/InheritedWidget/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /07/InheritedWidget/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /07/InheritedWidget/lib/inherited_widget/name_inherited_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 定义一个name共享父组件 4 | class NameInheritedWidget extends InheritedWidget { 5 | /// 共享状态 6 | final String name; 7 | 8 | /// 修改共享状态方法 9 | final Function onNameChange; 10 | 11 | /// 构造函数 12 | NameInheritedWidget({ 13 | Key key, 14 | @required Widget child, 15 | @required this.name, 16 | @required this.onNameChange, 17 | }) : super(key: key, child: child); 18 | 19 | @override 20 | bool updateShouldNotify(NameInheritedWidget old) { 21 | print(name != old.name); 22 | return name != old.name; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /07/InheritedWidget/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/pages/name_game.dart'; 4 | 5 | /// APP 核心入口文件 6 | void main() { 7 | runApp(MyApp()); 8 | } 9 | /// MyApp 核心入口界面 10 | class MyApp extends StatelessWidget { 11 | // This widget is the root of your application. 12 | @override 13 | Widget build(BuildContext context) { 14 | return MaterialApp( 15 | title: 'Two You', // APP 名字 16 | debugShowCheckedModeBanner: false, 17 | theme: ThemeData( 18 | primarySwatch: Colors.blue, // APP 主题 19 | ), 20 | home: Scaffold( 21 | appBar: AppBar( 22 | title: Text('Two You'), // 页面名字 23 | ), 24 | body: Center( 25 | child: NameGame(), 26 | ))); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /07/InheritedWidget/lib/widgets/name_game/random_name.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/inherited_widget/name_inherited_widget.dart'; 4 | 5 | /// 随机展示人名 6 | class RandomName extends StatelessWidget { 7 | /// 有状态类返回组件信息 8 | @override 9 | Widget build(BuildContext context) { 10 | final String name = 11 | (context.inheritFromWidgetOfExactType(NameInheritedWidget) 12 | as NameInheritedWidget) 13 | .name; 14 | final Function changeName = 15 | (context.inheritFromWidgetOfExactType(NameInheritedWidget) 16 | as NameInheritedWidget) 17 | .onNameChange; 18 | 19 | return FlatButton( 20 | child: Text(name), 21 | onPressed: () => changeName(), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /07/InheritedWidget/lib/widgets/name_game/test_other.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 欢迎人展示组件 4 | class TestOther extends StatelessWidget { 5 | /// 有状态类返回组件信息 6 | @override 7 | Widget build(BuildContext context) { 8 | print('test other build'); 9 | return Text('我是其他组件'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /07/InheritedWidget/lib/widgets/name_game/welcome.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/inherited_widget/name_inherited_widget.dart'; 4 | 5 | /// 欢迎人展示组件 6 | class Welcome extends StatelessWidget { 7 | /// 有状态类返回组件信息 8 | @override 9 | Widget build(BuildContext context) { 10 | print('welcome build'); 11 | final name = (context.inheritFromWidgetOfExactType(NameInheritedWidget) 12 | as NameInheritedWidget) 13 | .name; 14 | 15 | return Text('欢迎 $name'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /07/Overall/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /07/Overall/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /07/Overall/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /07/Overall/lib/model/like_num_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class LikeNumModel with ChangeNotifier { 5 | /// 声明私有变量 6 | int _likeNum = 0; 7 | 8 | /// 设置get方法 9 | int get value => _likeNum; 10 | 11 | /// 修改当前name,随机选取一个 12 | void like() { 13 | _likeNum++; 14 | notifyListeners(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /07/Overall/lib/model/name_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | /// name状态管理模块 6 | class NameModel with ChangeNotifier { 7 | /// 声明私有变量 8 | String _name = 'test flutter'; 9 | 10 | /// 设置get方法 11 | String get value => _name; 12 | 13 | /// 修改当前name,随机选取一个 14 | void changeName() { 15 | List nameList = ['flutter one', 'flutter two', 'flutter three']; 16 | int pos = Random().nextInt(3); 17 | 18 | if (_name != nameList[pos]) { 19 | _name = nameList[pos]; 20 | notifyListeners(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /07/Overall/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle([double multipleFontSize = 1]) { 12 | return TextStyle( 13 | color: Colors.lightBlueAccent, 14 | fontSize: baseFontSize * multipleFontSize, 15 | letterSpacing: 1, 16 | wordSpacing: 2, 17 | height: 1.2); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /07/Overall/lib/util/struct/article_summary_struct.dart: -------------------------------------------------------------------------------- 1 | /// 描述帖子的数据结构 2 | class ArticleSummaryStruct { 3 | /// 帖子标题 4 | final String title; 5 | 6 | /// 帖子概要 7 | final String summary; 8 | 9 | /// 帖子一张图 10 | final String articleImage; 11 | 12 | /// 点赞数量 13 | final int likeNum; 14 | 15 | /// 评论数量 16 | final int commentNum; 17 | 18 | /// 帖子内容 19 | final String content; 20 | 21 | /// 构造函数 22 | const ArticleSummaryStruct(this.title, this.summary, this.articleImage, 23 | this.likeNum, this.commentNum, 24 | [this.content]); 25 | } 26 | -------------------------------------------------------------------------------- /07/Overall/lib/util/struct/comment_info_struct.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info_struct.dart'; 2 | 3 | /// CommentListStruct数据结构描述信息 4 | class CommentInfoStruct { 5 | /// 用户的昵称 6 | final UserInfoStruct userInfo; 7 | 8 | /// 用户头像信息 9 | final String comment; 10 | 11 | /// 构造函数 12 | const CommentInfoStruct(this.userInfo, this.comment); 13 | } 14 | -------------------------------------------------------------------------------- /07/Overall/lib/util/struct/user_info_struct.dart: -------------------------------------------------------------------------------- 1 | /// userInfo数据结构描述信息 2 | class UserInfoStruct { 3 | /// 用户的昵称 4 | final String nickname; 5 | 6 | /// 用户头像信息 7 | final String headerImage; 8 | 9 | /// 构造函数 10 | const UserInfoStruct(this.nickname, this.headerImage); 11 | } 12 | -------------------------------------------------------------------------------- /07/Overall/lib/widgets/article_detail/article_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的帖子详情,内容模块 6 | /// 7 | /// [content]为帖子详情内容 8 | class ArticleContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Text( 21 | this.content, 22 | softWrap: true, 23 | style: TextStyles.commonStyle(), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /07/Overall/lib/widgets/article_detail/article_detail_like.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:two_you_friend/model/like_num_model.dart'; 5 | import 'package:two_you_friend/styles/text_syles.dart'; 6 | 7 | /// 帖子详情页的赞组件 8 | /// 9 | /// 包括点赞组件 icon ,以及组件点击效果 10 | /// 需要外部参数[likeNum],点赞数量 11 | class ArticleDetailLike extends StatelessWidget { 12 | /// 有状态类返回组件信息 13 | @override 14 | Widget build(BuildContext context) { 15 | final likeNumModel = Provider.of(context); 16 | 17 | return Column( 18 | crossAxisAlignment: CrossAxisAlignment.center, 19 | children: [ 20 | FlatButton( 21 | child: Icon(Icons.thumb_up, color: Colors.grey, size: 40), 22 | onPressed: () => likeNumModel.like(), 23 | ), 24 | Text( 25 | '${likeNumModel.value}', 26 | style: TextStyles.commonStyle(), 27 | ), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /07/Overall/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 帖子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /07/Provider/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /07/Provider/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /07/Provider/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /07/Provider/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/pages/name_game.dart'; 4 | 5 | 6 | /// APP 核心入口文件 7 | void main() { 8 | runApp(MyApp()); 9 | } 10 | 11 | /// MyApp 核心入口界面 12 | class MyApp extends StatelessWidget { 13 | 14 | // This widget is the root of your application. 15 | @override 16 | Widget build(BuildContext context) { 17 | return MaterialApp( 18 | title: 'Two You', // APP 名字 19 | debugShowCheckedModeBanner: false, 20 | theme: ThemeData( 21 | primarySwatch: Colors.blue, // APP 主题 22 | ), 23 | home: Scaffold( 24 | appBar: AppBar( 25 | title: Text('Two You'), // 页面名字 26 | ), 27 | body: Center( 28 | child: NameGame(), 29 | ))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /07/Provider/lib/model/name_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | /// name状态管理模块 6 | class NameModel with ChangeNotifier { 7 | /// 声明私有变量 8 | String _name = 'test flutter'; 9 | 10 | /// 设置get方法 11 | String get value => _name; 12 | 13 | /// 修改当前name,随机选取一个 14 | void changeName() { 15 | List nameList = ['flutter one', 'flutter two', 'flutter three']; 16 | int pos = Random().nextInt(3); 17 | 18 | if(_name != nameList[pos]) { 19 | _name = nameList[pos]; 20 | notifyListeners(); 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /07/Provider/lib/pages/name_game.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:two_you_friend/model/name_model.dart'; 5 | 6 | import 'package:two_you_friend/widgets/name_game/random_name.dart'; 7 | import 'package:two_you_friend/widgets/name_game/test_other.dart'; 8 | import 'package:two_you_friend/widgets/name_game/welcome.dart'; 9 | 10 | /// 测试随机名字游戏组件 11 | class NameGame extends StatelessWidget { 12 | /// 设置状态 name 13 | final name = NameModel(); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Column( 18 | children: [ 19 | Provider.value( 20 | child: ChangeNotifierProvider.value( 21 | value: name, 22 | child: Column( 23 | children: [ 24 | Welcome(), 25 | RandomName(), 26 | ], 27 | ), 28 | ), 29 | ), 30 | TestOther(), 31 | ], 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /07/Provider/lib/widgets/name_game/random_name.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:two_you_friend/model/name_model.dart'; 5 | 6 | /// 随机展示人名 7 | class RandomName extends StatelessWidget { 8 | /// 有状态类返回组件信息 9 | @override 10 | Widget build(BuildContext context) { 11 | final _name = Provider.of(context); 12 | 13 | print('random name build'); 14 | return FlatButton( 15 | child: Text(_name.value), 16 | onPressed: () => _name.changeName(), 17 | ); 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /07/Provider/lib/widgets/name_game/test_other.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 欢迎人展示组件 4 | class TestOther extends StatelessWidget { 5 | /// 有状态类返回组件信息 6 | @override 7 | Widget build(BuildContext context) { 8 | print('test other build'); 9 | return Text('我是其他组件'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /07/Provider/lib/widgets/name_game/welcome.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:two_you_friend/model/name_model.dart'; 5 | 6 | /// 欢迎人展示组件 7 | class Welcome extends StatelessWidget { 8 | 9 | /// 有状态类返回组件信息 10 | @override 11 | Widget build(BuildContext context) { 12 | final _name = Provider.of(context); 13 | 14 | print('welcome build'); 15 | return Text('欢迎 ${_name.value}'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /07/Redux/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /07/Redux/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /07/Redux/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /07/Redux/lib/pages/name_game.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:redux/redux.dart'; 3 | 4 | import 'package:two_you_friend/widgets/name_game/random_name.dart'; 5 | import 'package:two_you_friend/widgets/name_game/test_other.dart'; 6 | import 'package:two_you_friend/widgets/name_game/welcome.dart'; 7 | 8 | /// 测试随机名字游戏组件 9 | class NameGame extends StatelessWidget { 10 | /// store 11 | final Store store; 12 | 13 | /// 构造函数 14 | NameGame({Key key, this.store}) : super(key: key); 15 | 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Column( 20 | children: [ 21 | Welcome(store: store), 22 | RandomName(store: store), 23 | TestOther(), 24 | ], 25 | ); 26 | } 27 | } -------------------------------------------------------------------------------- /07/Redux/lib/states/name_states.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | /// name 状态管理类 4 | class NameStates { 5 | final String _name; 6 | /// getter 方法获取name 7 | get name => _name; 8 | 9 | /// 构造函数 10 | NameStates(this._name); 11 | 12 | /// 初始设置 13 | NameStates.initState() : _name = 'test flutter 1'; 14 | 15 | } 16 | 17 | /// 定义 name state 对应的状态修改 action 18 | /// 19 | /// [NameActions.changeName] 为修改当前 name 20 | enum NameActions { 21 | /// 修改 name 的 state 22 | changeName 23 | } 24 | 25 | /// reducer 方法,触发组件更新 26 | NameStates reducer(NameStates state, action){ 27 | if (action == NameActions.changeName) { 28 | return changeName(); 29 | } 30 | return state; 31 | } 32 | 33 | /// 修改当前name,随机选取一个 34 | NameStates changeName() { 35 | List nameList = ['flutter one', 'flutter two', 'flutter three']; 36 | int pos = Random().nextInt(3); 37 | return NameStates(nameList[pos]); 38 | } -------------------------------------------------------------------------------- /07/Redux/lib/widgets/name_game/test_other.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 欢迎人展示组件 4 | class TestOther extends StatelessWidget { 5 | /// 有状态类返回组件信息 6 | @override 7 | Widget build(BuildContext context) { 8 | print('test other build'); 9 | return Text('我是其他组件'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /07/Redux/lib/widgets/name_game/welcome.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_redux/flutter_redux.dart'; 3 | import 'package:redux/redux.dart'; 4 | 5 | import 'package:two_you_friend/states/name_states.dart'; 6 | 7 | 8 | /// 欢迎人展示组件 9 | class Welcome extends StatelessWidget { 10 | /// store 11 | final Store store; 12 | 13 | /// 构造函数 14 | Welcome({Key key, this.store}) : super(key: key); 15 | 16 | /// 有状态类返回组件信息 17 | @override 18 | Widget build(BuildContext context) { 19 | print('welcome build'); 20 | return StoreConnector( 21 | converter: (store) => store.state.name.toString(), 22 | builder: (context, name) { 23 | return Text('欢迎 $name'); 24 | }, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /08/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /08/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /08/build/testfile.dill.track.dill: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/love-flutter/flutter-column/a12e71b542011d52ba16a195e97995d71d494ba6/08/build/testfile.dill.track.dill -------------------------------------------------------------------------------- /08/format_check.sh: -------------------------------------------------------------------------------- 1 | # 代码美化 2 | dartfmt -w --fix lib/ 3 | 4 | # 代码规范检查 5 | dartanalyzer lib -------------------------------------------------------------------------------- /08/lib/model/like_num_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class LikeNumModel with ChangeNotifier { 5 | /// 声明私有变量 6 | int _likeNum = 0; 7 | 8 | /// 设置get方法 9 | int get value => _likeNum; 10 | 11 | /// 修改当前name,随机选取一个 12 | void like() { 13 | print('like succes'); 14 | _likeNum++; 15 | notifyListeners(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /08/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle([double multipleFontSize = 1]) { 12 | return TextStyle( 13 | color: Colors.lightBlueAccent, 14 | fontSize: baseFontSize * multipleFontSize, 15 | letterSpacing: 1, 16 | wordSpacing: 2, 17 | height: 1.2); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /08/lib/util/struct/article_summary_struct.dart: -------------------------------------------------------------------------------- 1 | /// 描述帖子的数据结构 2 | class ArticleSummaryStruct { 3 | /// 帖子标题 4 | final String title; 5 | 6 | /// 帖子概要 7 | final String summary; 8 | 9 | /// 帖子一张图 10 | final String articleImage; 11 | 12 | /// 点赞数量 13 | final int likeNum; 14 | 15 | /// 评论数量 16 | final int commentNum; 17 | 18 | /// 帖子内容 19 | final String content; 20 | 21 | /// 构造函数 22 | const ArticleSummaryStruct(this.title, this.summary, this.articleImage, 23 | this.likeNum, this.commentNum, 24 | [this.content]); 25 | } 26 | -------------------------------------------------------------------------------- /08/lib/util/struct/comment_info_struct.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info_struct.dart'; 2 | 3 | /// CommentListStruct数据结构描述信息 4 | class CommentInfoStruct { 5 | /// 用户的昵称 6 | final UserInfoStruct userInfo; 7 | 8 | /// 用户头像信息 9 | final String comment; 10 | 11 | /// 构造函数 12 | const CommentInfoStruct(this.userInfo, this.comment); 13 | } 14 | -------------------------------------------------------------------------------- /08/lib/util/struct/user_info_struct.dart: -------------------------------------------------------------------------------- 1 | /// userInfo数据结构描述信息 2 | class UserInfoStruct { 3 | /// 用户的昵称 4 | final String nickname; 5 | 6 | /// 用户头像信息 7 | final String headerImage; 8 | 9 | /// 构造函数 10 | const UserInfoStruct(this.nickname, this.headerImage); 11 | } 12 | -------------------------------------------------------------------------------- /08/lib/widgets/article_detail/article_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的帖子详情,内容模块 6 | /// 7 | /// [content]为帖子详情内容 8 | class ArticleContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Text( 21 | this.content, 22 | softWrap: true, 23 | style: TextStyles.commonStyle(), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /08/lib/widgets/article_detail/article_detail_like.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:two_you_friend/model/like_num_model.dart'; 5 | import 'package:two_you_friend/styles/text_syles.dart'; 6 | 7 | /// 帖子详情页的赞组件 8 | /// 9 | /// 包括点赞组件 icon ,以及组件点击效果 10 | /// 需要外部参数[likeNum],点赞数量 11 | class ArticleDetailLike extends StatelessWidget { 12 | /// 有状态类返回组件信息 13 | @override 14 | Widget build(BuildContext context) { 15 | final likeNumModel = Provider.of(context); 16 | 17 | return Column( 18 | crossAxisAlignment: CrossAxisAlignment.center, 19 | children: [ 20 | FlatButton( 21 | child: Icon(Icons.thumb_up, color: Colors.grey, size: 40), 22 | onPressed: () => likeNumModel.like(), 23 | ), 24 | Text( 25 | '${likeNumModel.value}', 26 | style: TextStyles.commonStyle(), 27 | ), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /08/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 帖子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /08/test/model/like_num_model_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/model/like_num_model.dart'; 4 | 5 | void main() { 6 | final LikeNumModel likeNumModel = LikeNumModel(); 7 | 8 | test('test like model value', () { 9 | expect(likeNumModel.value, 0); 10 | }); 11 | 12 | test('test like model like method', () { 13 | likeNumModel.like(); 14 | expect(likeNumModel.value, 1); 15 | 16 | likeNumModel.like(); 17 | expect(likeNumModel.value, 2); 18 | }); 19 | } -------------------------------------------------------------------------------- /08/test/util/struct/article_summary_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/article_summary_struct.dart'; 4 | 5 | void main() { 6 | final ArticleSummaryStruct articleSummary = ArticleSummaryStruct('test', 'test summary', 'http://test.com', 1, 2); 7 | 8 | test('test article summary', () { 9 | expect(articleSummary.title, 'test'); 10 | expect(articleSummary.summary, 'test summary'); 11 | expect(articleSummary.articleImage, 'http://test.com'); 12 | expect(articleSummary.likeNum, 1); 13 | expect(articleSummary.commentNum, 2); 14 | }); 15 | 16 | } -------------------------------------------------------------------------------- /08/test/util/struct/comment_info_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/comment_info_struct.dart'; 4 | import 'package:two_you_friend/util/struct/user_info_struct.dart'; 5 | 6 | void main() { 7 | final UserInfoStruct userInfo = UserInfoStruct('test', 'http://test.com'); 8 | 9 | test('test comment info', () { 10 | final CommentInfoStruct commentInfo = 11 | CommentInfoStruct(userInfo, 'comment test'); 12 | 13 | expect(commentInfo.comment == 'comment test', true); 14 | expect(commentInfo.userInfo.nickname == 'test', true); 15 | expect(commentInfo.userInfo.headerImage, 'http://test.com'); 16 | }); 17 | 18 | } -------------------------------------------------------------------------------- /08/test/util/struct/user_info_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/user_info_struct.dart'; 4 | 5 | void main() { 6 | final UserInfoStruct userInfo = UserInfoStruct('test', 'http://test.com'); 7 | 8 | test('test userinfo', () { 9 | expect(userInfo.nickname == 'test', true); 10 | expect(userInfo.headerImage, 'http://test.com'); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /08/test/widgets/article_detail/article_content_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/widgets/article_detail/article_content.dart'; 5 | 6 | void main() { 7 | testWidgets('test article content', (WidgetTester tester) async { 8 | final Widget testWidgets = ArticleContent(content: 'test content'); 9 | await tester.pumpWidget( 10 | new MaterialApp( 11 | home: testWidgets 12 | ) 13 | ); 14 | 15 | expect(find.text('test content'), findsOneWidget); 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /08/test/widgets/common/banner_info_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:image_test_utils/image_test_utils.dart'; 4 | 5 | import 'package:two_you_friend/widgets/common/banner_info.dart'; 6 | 7 | void main() { 8 | testWidgets('test banner info', (WidgetTester tester) async { 9 | provideMockedNetworkImages(() async { 10 | final Widget testWidgets = BannerInfo( 11 | bannerImage: 'https://i.pinimg.com/originals/e0/64/4b/e0644bd2f13db50d0ef6a4df5a756fd9.png' 12 | ); 13 | await tester.pumpWidget( 14 | new MaterialApp( 15 | home: testWidgets 16 | ) 17 | ); 18 | 19 | expect(find.byWidget(testWidgets), findsOneWidget); 20 | }); 21 | }); 22 | } -------------------------------------------------------------------------------- /08/test/widgets/home_page/article_bottom_bar_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:image_test_utils/image_test_utils.dart'; 4 | 5 | import 'package:two_you_friend/widgets/home_page/article_bottom_bar.dart'; 6 | 7 | void main() { 8 | testWidgets('test article bottom bar', (WidgetTester tester) async { 9 | provideMockedNetworkImages(() async { 10 | final Widget testWidgets = ArticleBottomBar( 11 | nickname: 'test', 12 | headerImage: 'https://i.pinimg.com/originals/e0/64/4b/e0644bd2f13db50d0ef6a4df5a756fd9.png', 13 | commentNum: 1 14 | ); 15 | await tester.pumpWidget( 16 | new MaterialApp( 17 | home: testWidgets 18 | ) 19 | ); 20 | 21 | expect(find.text('test'), findsOneWidget); 22 | expect(find.text('1'), findsOneWidget); 23 | expect(find.byWidget(testWidgets), findsOneWidget); 24 | }); 25 | }); 26 | } -------------------------------------------------------------------------------- /10/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /10/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /10/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /10/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | 15 | return WebviewScaffold( 16 | url: url, 17 | appBar: AppBar( 18 | backgroundColor: Colors.blue, 19 | ), 20 | ); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /10/lib/pages/home_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 首页 4 | class HomePageIndex extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | // TODO: implement build 8 | return Text('I am home page'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /10/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/tools/json_config.dart'; 4 | 5 | /// 首页 6 | class UserPageIndex extends StatelessWidget { 7 | /// 用户 ID 信息 8 | final String userId; 9 | 10 | /// 构造函数 11 | const UserPageIndex({Key key, this.userId}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | Map dataInfo = JsonConfig.objectToMap( 16 | ModalRoute.of(context).settings.arguments 17 | ); 18 | 19 | // TODO: implement build 20 | return Text('I am use page ${dataInfo['userId']}'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /10/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future getConfig(String fileName) async { 14 | if(cache[fileName] != null){ 15 | return cache[fileName]; 16 | } 17 | final jsonString = await rootBundle.loadString('assets/json/${fileName}.dart'); 18 | try { 19 | var jsonRet = json.decode(jsonString); 20 | cache[fileName] = jsonRet; 21 | return jsonRet; 22 | } catch (err){ 23 | // @todo add report log 24 | return false; 25 | } 26 | } 27 | 28 | /// object convert to map data 29 | static Map objectToMap(Object data) { 30 | return jsonDecode(JsonCodec().encode(data)) as Map; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /10/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = HomePageIndex(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /10/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = UserPageIndex(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /11/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /11/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /11/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /11/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/project_router.dart'; 4 | // 底部导航栏 5 | //import 'package:two_you_friend/pages/entrance_bottom_bar.dart'; 6 | // 顶部导航栏 7 | import 'package:two_you_friend/pages/entrance_top_bar.dart'; 8 | 9 | /// APP 核心入口文件 10 | void main() { 11 | runApp(MyApp()); 12 | } 13 | 14 | /// MyApp 核心入口界面 15 | class MyApp extends StatelessWidget { 16 | // This widget is the root of your application. 17 | @override 18 | Widget build(BuildContext context) { 19 | return MaterialApp( 20 | title: 'Two You', // APP 名字 21 | debugShowCheckedModeBanner: false, 22 | theme: ThemeData( 23 | primarySwatch: Colors.blue, // APP 主题 24 | ), 25 | routes: ProjectRouter().registerRouter(), 26 | home: Entrance()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /11/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /11/lib/pages/home_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 首页 4 | class HomePageIndex extends StatelessWidget { 5 | /// 构造函数 6 | const HomePageIndex({Key key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | // TODO: implement build 11 | return Text('I am home page'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /11/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/tools/json_config.dart'; 4 | 5 | /// 首页 6 | class UserPageIndex extends StatelessWidget { 7 | /// 用户 ID 信息 8 | final String userId; 9 | 10 | /// 构造函数 11 | const UserPageIndex({Key key, this.userId}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | String myUserId = userId; 16 | if (ModalRoute.of(context).settings.arguments != null) { 17 | Map dataInfo = 18 | JsonConfig.objectToMap(ModalRoute.of(context).settings.arguments); 19 | myUserId = dataInfo['userId'].toString(); 20 | } 21 | 22 | // TODO: implement build 23 | return Text('I am use page ${myUserId}'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /11/lib/util/struct/router_struct.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class RouterStruct { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 默认构造函数 15 | const RouterStruct(this.widget, this.entranceIndex, this.params); 16 | } 17 | -------------------------------------------------------------------------------- /11/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future getConfig(String fileName) async { 14 | if (cache[fileName] != null) { 15 | return cache[fileName]; 16 | } 17 | final jsonString = 18 | await rootBundle.loadString('assets/json/${fileName}.dart'); 19 | try { 20 | var jsonRet = json.decode(jsonString); 21 | cache[fileName] = jsonRet; 22 | return jsonRet; 23 | } catch (err) { 24 | // @todo add report log 25 | return false; 26 | } 27 | } 28 | 29 | /// object convert to map data 30 | static Map objectToMap(Object data) { 31 | return jsonDecode(JsonCodec().encode(data)) as Map; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /11/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /11/test/pages/entrance_bottom_bar_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance_bottom_bar.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance_bottom_bar.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = Entrance(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /11/test/pages/entrance_top_bar_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance_top_bar.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance_top_bar.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = Entrance(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /11/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = HomePageIndex(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /11/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = UserPageIndex(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /11/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router_struct.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router_struct.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /12/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /12/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /12/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /12/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /12/lib/api/user_info/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 获取用户接口信息 4 | class ApiUserInfoIndex { 5 | /// 根据用户id拉取用户信息 6 | static StructUserInfo getOneById(String id) { 7 | return StructUserInfo('1001', 'test 001', 8 | 'http://image.biaobaiju.com/uploads/20180211/00/1518279965-WhqyaQodpn.jpg'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /12/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /12/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/tools/json_config.dart'; 4 | 5 | /// 首页 6 | class UserPageIndex extends StatelessWidget { 7 | /// 用户 ID 信息 8 | final String userId; 9 | 10 | /// 构造函数 11 | const UserPageIndex({Key key, this.userId}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | String myUserId = userId; 16 | if (ModalRoute.of(context).settings.arguments != null) { 17 | Map dataInfo = 18 | JsonConfig.objectToMap(ModalRoute.of(context).settings.arguments); 19 | myUserId = dataInfo['userId'].toString(); 20 | } 21 | 22 | // TODO: implement build 23 | return Text('I am use page ${myUserId}'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /12/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /12/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | } 19 | -------------------------------------------------------------------------------- /12/lib/util/struct/content_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// router 配置数据结构 4 | /// 5 | /// { 6 | /// "id" : "string", 7 | /// "title" : "string", 8 | /// "summary" : "string", 9 | /// "detailInfo" : "string", 10 | /// "uid" : "string", 11 | /// "userInfo" : "StructUserInfo", 12 | /// "articleImage" : "string", 13 | /// "likeNum" : "int", 14 | /// "commentNum" : "int" 15 | /// } 16 | class StructContentDetail { 17 | /// 帖子id 18 | final String id; 19 | 20 | /// 标题 21 | final String title; 22 | 23 | /// 简要 24 | final String summary; 25 | 26 | /// 主要内容 27 | final String detailInfo; 28 | 29 | /// 作者ID 30 | final String uid; 31 | 32 | /// 用户信息 33 | final StructUserInfo userInfo; 34 | 35 | /// 文章图片标识 36 | final String articleImage; 37 | 38 | /// 点赞数 39 | final int likeNum; 40 | 41 | /// 评论数量 42 | final int commentNum; 43 | 44 | /// 默认构造函数 45 | const StructContentDetail(this.id, this.title, this.summary, this.detailInfo, 46 | this.uid, this.likeNum, this.commentNum, this.articleImage, 47 | {this.userInfo}); 48 | } 49 | -------------------------------------------------------------------------------- /12/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 默认构造函数 15 | const StructRouter(this.widget, this.entranceIndex, this.params); 16 | } 17 | -------------------------------------------------------------------------------- /12/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | } 21 | -------------------------------------------------------------------------------- /12/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future getConfig(String fileName) async { 14 | if (cache[fileName] != null) { 15 | return cache[fileName]; 16 | } 17 | final jsonString = 18 | await rootBundle.loadString('assets/json/${fileName}.dart'); 19 | try { 20 | var jsonRet = json.decode(jsonString); 21 | cache[fileName] = jsonRet; 22 | return jsonRet; 23 | } catch (err) { 24 | // @todo add report log 25 | return false; 26 | } 27 | } 28 | 29 | /// object convert to map data 30 | static Map objectToMap(Object data) { 31 | return jsonDecode(JsonCodec().encode(data)) as Map; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /12/lib/widgets/article_detail/article_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的帖子详情,内容模块 6 | /// 7 | /// [content]为帖子详情内容 8 | class ArticleContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Text( 21 | this.content, 22 | softWrap: true, 23 | style: TextStyles.commonStyle(), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /12/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 帖子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /12/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /12/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /12/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /12/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = UserPageIndex(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /12/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /13/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /13/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /13/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /13/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /13/lib/api/user_info/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 获取用户接口信息 4 | class ApiUserInfoIndex { 5 | /// 根据用户id拉取用户信息 6 | static StructUserInfo getOneById(String id) { 7 | return StructUserInfo('1001', 'test 001', 8 | 'http://image.biaobaiju.com/uploads/20180211/00/1518279965-WhqyaQodpn.jpg'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /13/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /13/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/tools/json_config.dart'; 4 | 5 | /// 首页 6 | class UserPageIndex extends StatelessWidget { 7 | /// 用户 ID 信息 8 | final String userId; 9 | 10 | /// 构造函数 11 | const UserPageIndex({Key key, this.userId}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | String myUserId = userId; 16 | if (ModalRoute.of(context).settings.arguments != null) { 17 | Map dataInfo = 18 | JsonConfig.objectToMap(ModalRoute.of(context).settings.arguments); 19 | myUserId = dataInfo['userId'].toString(); 20 | } 21 | 22 | // TODO: implement build 23 | return Text('I am use page ${myUserId}'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /13/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /13/lib/util/struct/api_ret_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/content_detail.dart'; 2 | 3 | /// api 拉取content list返回结构 4 | /// 5 | /// { 6 | /// "ret" : 0, 7 | /// "message" : "success", 8 | /// "hasMore" : true, 9 | /// "lastId" : null, 10 | /// } 11 | class StructApiContentListRetInfo { 12 | /// 用户的昵称 13 | final int ret; 14 | 15 | /// 用户头像信息 16 | final String message; 17 | 18 | /// 是否还有更多 19 | final bool hasMore; 20 | 21 | /// 最后一个id 22 | final String lastId; 23 | 24 | /// 具体的content list 25 | final List data; 26 | 27 | /// 构造函数 28 | const StructApiContentListRetInfo( 29 | this.ret, this.message, this.hasMore, this.lastId, this.data); 30 | } 31 | -------------------------------------------------------------------------------- /13/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | } 19 | -------------------------------------------------------------------------------- /13/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 默认构造函数 15 | const StructRouter(this.widget, this.entranceIndex, this.params); 16 | } 17 | -------------------------------------------------------------------------------- /13/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | } 21 | -------------------------------------------------------------------------------- /13/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future getConfig(String fileName) async { 14 | if (cache[fileName] != null) { 15 | return cache[fileName]; 16 | } 17 | final jsonString = 18 | await rootBundle.loadString('assets/json/${fileName}.dart'); 19 | try { 20 | var jsonRet = json.decode(jsonString); 21 | cache[fileName] = jsonRet; 22 | return jsonRet; 23 | } catch (err) { 24 | // @todo add report log 25 | return false; 26 | } 27 | } 28 | 29 | /// object convert to map data 30 | static Map objectToMap(Object data) { 31 | return jsonDecode(JsonCodec().encode(data)) as Map; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /13/lib/widgets/article_detail/article_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子详情,内容模块 6 | /// 7 | /// [content]为贴子详情内容 8 | class ArticleContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Text( 21 | this.content, 22 | softWrap: true, 23 | style: TextStyles.commonStyle(), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /13/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 贴子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /13/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /13/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /13/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /13/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = UserPageIndex(); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /13/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /14/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /14/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /14/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /14/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /14/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /14/lib/api/user_info/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 获取用户接口信息 4 | class ApiUserInfoIndex { 5 | /// 根据用户id拉取用户信息 6 | static StructUserInfo getOneById(String id) { 7 | return StructUserInfo('1001', 'test 001', 8 | 'http://image.biaobaiju.com/uploads/20180211/00/1518279965-WhqyaQodpn.jpg'); 9 | } 10 | 11 | /// 根据用户id拉取用户信息 12 | static StructUserInfo getSelfUserInfo() { 13 | return StructUserInfo('1001', 'test 001', 14 | 'http://image.biaobaiju.com/uploads/20180211/00/1518279965-WhqyaQodpn.jpg'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /14/lib/api/user_info/message.dart: -------------------------------------------------------------------------------- 1 | /// 获取用户消息相关 2 | class ApiUserInfoMessage { 3 | /// 获取自己的个人信息 4 | static int getUnReadMessageNum() { 5 | return 18; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /14/lib/model/new_message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class NewMessageModel with ChangeNotifier { 5 | /// 系统未读新消息数 6 | int newMessageNum; 7 | 8 | /// 构造函数 9 | NewMessageModel({this.newMessageNum}); 10 | 11 | /// 获取未读消息 12 | int get value => newMessageNum; 13 | 14 | /// 设置已经阅读消息 15 | void readNewMessage() { 16 | if (newMessageNum == 0) { 17 | return; 18 | } 19 | newMessageNum = 0; 20 | notifyListeners(); 21 | } 22 | 23 | /// 接口异步返回重新通知 24 | void setNewMessageNum(int unReadNum) { 25 | if (unReadNum == null || unReadNum == 0) { 26 | return; 27 | } 28 | newMessageNum = unReadNum; 29 | notifyListeners(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /14/lib/model/user_info_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/user_info.dart'; 4 | 5 | /// name状态管理模块 6 | class UserInfoModel with ChangeNotifier { 7 | /// 系统用户个人信息 8 | final StructUserInfo myUserInfo; 9 | 10 | /// 构造函数 11 | UserInfoModel({this.myUserInfo}); 12 | 13 | /// 获取用户信息 14 | StructUserInfo get value => myUserInfo; 15 | } 16 | -------------------------------------------------------------------------------- /14/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /14/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/user_page/button_list.dart'; 4 | import 'package:two_you_friend/widgets/user_page/header.dart'; 5 | 6 | /// 首页 7 | class UserPageIndex extends StatelessWidget { 8 | /// 构造函数 9 | const UserPageIndex(); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | UserPageHeader(), 16 | Expanded( 17 | child: UserPageButtonList(), 18 | ), 19 | ], 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /14/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /14/lib/util/struct/api_ret_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/content_detail.dart'; 2 | 3 | /// api 拉取content list返回结构 4 | /// 5 | /// { 6 | /// "ret" : 0, 7 | /// "message" : "success", 8 | /// "hasMore" : true, 9 | /// "lastId" : null, 10 | /// } 11 | class StructApiContentListRetInfo { 12 | /// 用户的昵称 13 | final int ret; 14 | 15 | /// 用户头像信息 16 | final String message; 17 | 18 | /// 是否还有更多 19 | final bool hasMore; 20 | 21 | /// 最后一个id 22 | final String lastId; 23 | 24 | /// 具体的content list 25 | final List data; 26 | 27 | /// 构造函数 28 | const StructApiContentListRetInfo( 29 | this.ret, this.message, this.hasMore, this.lastId, this.data); 30 | } 31 | -------------------------------------------------------------------------------- /14/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | } 19 | -------------------------------------------------------------------------------- /14/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 默认构造函数 15 | const StructRouter(this.widget, this.entranceIndex, this.params); 16 | } 17 | -------------------------------------------------------------------------------- /14/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | } 21 | -------------------------------------------------------------------------------- /14/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future getConfig(String fileName) async { 14 | if (cache[fileName] != null) { 15 | return cache[fileName]; 16 | } 17 | final jsonString = 18 | await rootBundle.loadString('assets/json/${fileName}.dart'); 19 | try { 20 | var jsonRet = json.decode(jsonString); 21 | cache[fileName] = jsonRet; 22 | return jsonRet; 23 | } catch (err) { 24 | // @todo add report log 25 | return false; 26 | } 27 | } 28 | 29 | /// object convert to map data 30 | static Map objectToMap(Object data) { 31 | return jsonDecode(JsonCodec().encode(data)) as Map; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /14/lib/widgets/article_detail/article_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子详情,内容模块 6 | /// 7 | /// [content]为贴子详情内容 8 | class ArticleContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Text( 21 | this.content, 22 | softWrap: true, 23 | style: TextStyles.commonStyle(), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /14/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 贴子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /14/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /14/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /14/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /14/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /14/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /15/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application test. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /15/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /15/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /15/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /15/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /15/lib/api/user_info/message.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/model/new_message_model.dart'; 2 | import 'package:two_you_friend/util/struct/api_ret_info.dart'; 3 | import 'package:two_you_friend/util/tools/call_server.dart'; 4 | 5 | /// 获取用户消息相关 6 | class ApiUserInfoMessage { 7 | /// 获取自己的个人信息 8 | static Future getUnReadMessageNum( 9 | NewMessageModel newMessageModel) async { 10 | Map retJson = await CallServer.get('newMessage'); 11 | StructApiRetInfo retInfo = StructApiRetInfo.fromJson(retJson); 12 | if (retInfo.ret != 0 || retInfo.data == null) { 13 | return; 14 | } 15 | Map dataInfo = retInfo.data as Map; 16 | 17 | if (dataInfo == null || dataInfo['unread_num'] == null) { 18 | return; 19 | } 20 | newMessageModel.setNewMessageNum(dataInfo['unread_num'] as int); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /15/lib/model/new_message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class NewMessageModel with ChangeNotifier { 5 | /// 系统未读新消息数 6 | int newMessageNum; 7 | 8 | /// 构造函数 9 | NewMessageModel({this.newMessageNum}); 10 | 11 | /// 获取未读消息 12 | int get value => newMessageNum; 13 | 14 | /// 设置已经阅读消息 15 | void readNewMessage() { 16 | if (newMessageNum == 0) { 17 | return; 18 | } 19 | newMessageNum = 0; 20 | notifyListeners(); 21 | } 22 | 23 | /// 接口异步返回重新通知 24 | void setNewMessageNum(int unReadNum) { 25 | if (unReadNum == null || unReadNum == 0) { 26 | return; 27 | } 28 | newMessageNum = unReadNum; 29 | notifyListeners(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /15/lib/model/user_info_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/user_info.dart'; 4 | 5 | /// name状态管理模块 6 | class UserInfoModel with ChangeNotifier { 7 | /// 系统用户个人信息 8 | final StructUserInfo myUserInfo; 9 | 10 | /// 构造函数 11 | UserInfoModel({this.myUserInfo}); 12 | 13 | /// 获取用户信息 14 | StructUserInfo get value => myUserInfo; 15 | } 16 | -------------------------------------------------------------------------------- /15/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /15/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/user_page/button_list.dart'; 4 | import 'package:two_you_friend/widgets/user_page/header.dart'; 5 | 6 | /// 首页 7 | class UserPageIndex extends StatelessWidget { 8 | /// 构造函数 9 | const UserPageIndex(); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | UserPageHeader(), 16 | Expanded( 17 | child: UserPageButtonList(), 18 | ), 19 | ], 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /15/lib/protos/user_info.pbenum.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: protos/user_info.proto 4 | // 5 | // @dart = 2.3 6 | // ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type 7 | -------------------------------------------------------------------------------- /15/lib/protos/user_info.pbjson.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: protos/user_info.proto 4 | // 5 | // @dart = 2.3 6 | // ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type 7 | 8 | const UserInfoRsp$json = { 9 | '1': 'UserInfoRsp', 10 | '2': [ 11 | {'1': 'nickName', '3': 1, '4': 1, '5': 9, '10': 'nickName'}, 12 | {'1': 'headerUrl', '3': 2, '4': 1, '5': 9, '10': 'headerUrl'}, 13 | {'1': 'uid', '3': 3, '4': 1, '5': 9, '10': 'uid'}, 14 | ], 15 | }; 16 | -------------------------------------------------------------------------------- /15/lib/protos/user_info.pbserver.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: protos/user_info.proto 4 | // 5 | // @dart = 2.3 6 | // ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type 7 | 8 | export 'user_info.pb.dart'; 9 | -------------------------------------------------------------------------------- /15/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /15/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | 19 | /// 将json数据转化为对象数据 20 | StructCommentInfo.fromJson(Map json) 21 | : comment = json['comment'] as String, 22 | userInfo = 23 | StructUserInfo.fromJson(json['userInfo'] as Map); 24 | 25 | /// 将对象转化为json数据 26 | static Map toJson(StructCommentInfo commentInfo) => { 27 | 'comment': commentInfo.comment, 28 | 'userInfo': StructUserInfo.toJson(commentInfo.userInfo) 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /15/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 默认构造函数 15 | const StructRouter(this.widget, this.entranceIndex, this.params); 16 | } 17 | -------------------------------------------------------------------------------- /15/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | 21 | /// 将json数据转化为对象数据 22 | StructUserInfo.fromJson(Map json) 23 | : uid = json['uid'] as String, 24 | headerUrl = json['headerUrl'] as String, 25 | nickName = json['nickName'] as String; 26 | 27 | /// 将对象转化为json数据 28 | static Map toJson(StructUserInfo userInfo) => { 29 | 'uid': userInfo.uid, 30 | 'headerUrl': userInfo.headerUrl, 31 | 'nickName': userInfo.nickName 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /15/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map> cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future> getConfig(String fileName) async { 14 | final jsonString = 15 | await rootBundle.loadString('assets/json/${fileName}.json'); 16 | try { 17 | Map jsonRet = 18 | json.decode(jsonString) as Map; 19 | return jsonRet; 20 | } catch (err) { 21 | print(err); 22 | return null; 23 | } 24 | } 25 | 26 | /// object convert to map data 27 | static Map objectToMap(Object data) { 28 | return jsonDecode(JsonCodec().encode(data)) as Map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /15/lib/widgets/article_detail/article_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子详情,内容模块 6 | /// 7 | /// [content]为贴子详情内容 8 | class ArticleContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Text( 21 | this.content, 22 | softWrap: true, 23 | style: TextStyles.commonStyle(), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /15/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 贴子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /15/protos/user_info.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "pro.two_you_friend"; 4 | 5 | message UserInfoRsp { 6 | string nickName = 1; 7 | string headerUrl = 2; 8 | string uid = 3; 9 | } -------------------------------------------------------------------------------- /15/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /15/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /15/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /15/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /15/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /16/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /16/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /16/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /16/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /16/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /16/lib/api/user_info/message.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/model/new_message_model.dart'; 2 | import 'package:two_you_friend/util/struct/api_ret_info.dart'; 3 | import 'package:two_you_friend/util/tools/call_server.dart'; 4 | 5 | /// 获取用户消息相关 6 | class ApiUserInfoMessage { 7 | /// 获取自己的个人信息 8 | static Future getUnReadMessageNum( 9 | NewMessageModel newMessageModel) async { 10 | Map retJson = await CallServer.get('newMessage'); 11 | if(retJson == null || retJson['ret'] == false){ 12 | return; 13 | } 14 | StructApiRetInfo retInfo = StructApiRetInfo.fromJson(retJson); 15 | if (retInfo.ret != 0 || retInfo.data == null) { 16 | return; 17 | } 18 | Map dataInfo = retInfo.data as Map; 19 | 20 | if (dataInfo == null || dataInfo['unread_num'] == null) { 21 | return; 22 | } 23 | newMessageModel.setNewMessageNum(dataInfo['unread_num'] as int); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /16/lib/model/new_message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class NewMessageModel with ChangeNotifier { 5 | /// 系统未读新消息数 6 | int newMessageNum; 7 | 8 | /// 构造函数 9 | NewMessageModel({this.newMessageNum}); 10 | 11 | /// 获取未读消息 12 | int get value => newMessageNum; 13 | 14 | /// 设置已经阅读消息 15 | void readNewMessage() { 16 | if (newMessageNum == 0) { 17 | return; 18 | } 19 | newMessageNum = 0; 20 | notifyListeners(); 21 | } 22 | 23 | /// 接口异步返回重新通知 24 | void setNewMessageNum(int unReadNum) { 25 | if (unReadNum == null || unReadNum == 0) { 26 | return; 27 | } 28 | newMessageNum = unReadNum; 29 | notifyListeners(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /16/lib/model/user_info_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/user_info.dart'; 4 | 5 | /// name状态管理模块 6 | class UserInfoModel with ChangeNotifier { 7 | /// 系统用户个人信息 8 | final StructUserInfo myUserInfo; 9 | 10 | /// 构造函数 11 | UserInfoModel({this.myUserInfo}); 12 | 13 | /// 获取用户信息 14 | StructUserInfo get value => myUserInfo; 15 | } 16 | -------------------------------------------------------------------------------- /16/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /16/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/user_page/button_list.dart'; 4 | import 'package:two_you_friend/widgets/user_page/header.dart'; 5 | 6 | /// 首页 7 | class UserPageIndex extends StatelessWidget { 8 | /// 构造函数 9 | const UserPageIndex(); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | UserPageHeader(), 16 | Divider(height: 1.0, indent: 70, color: Colors.grey), 17 | Expanded( 18 | child: UserPageButtonList(), 19 | ), 20 | ], 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /16/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /16/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | 19 | /// 将json数据转化为对象数据 20 | StructCommentInfo.fromJson(Map json) 21 | : comment = json['comment'] as String, 22 | userInfo = 23 | StructUserInfo.fromJson(json['userInfo'] as Map); 24 | 25 | /// 将对象转化为json数据 26 | static Map toJson(StructCommentInfo commentInfo) => { 27 | 'comment': commentInfo.comment, 28 | 'userInfo': StructUserInfo.toJson(commentInfo.userInfo) 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /16/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 是否需要默认 Scaffold 15 | final bool needScaffold; 16 | 17 | /// 默认构造函数 18 | const StructRouter( 19 | this.widget, 20 | this.entranceIndex, 21 | this.params, 22 | this.needScaffold 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /16/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | 21 | /// 将json数据转化为对象数据 22 | StructUserInfo.fromJson(Map json) 23 | : uid = json['uid'] as String, 24 | headerUrl = json['headerUrl'] as String, 25 | nickName = json['nickName'] as String; 26 | 27 | /// 将对象转化为json数据 28 | static Map toJson(StructUserInfo userInfo) => { 29 | 'uid': userInfo.uid, 30 | 'headerUrl': userInfo.headerUrl, 31 | 'nickName': userInfo.nickName 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /16/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map> cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future> getConfig(String fileName) async { 14 | final jsonString = 15 | await rootBundle.loadString('assets/json/${fileName}.json'); 16 | try { 17 | Map jsonRet = 18 | json.decode(jsonString) as Map; 19 | return jsonRet; 20 | } catch (err) { 21 | print(err); 22 | return null; 23 | } 24 | } 25 | 26 | /// object convert to map data 27 | static Map objectToMap(Object data) { 28 | return jsonDecode(JsonCodec().encode(data)) as Map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /16/lib/widgets/article_detail/content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子详情,内容模块 6 | /// 7 | /// [content]为贴子详情内容 8 | class ArticleDetailContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleDetailContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Expanded( 18 | child: Text( 19 | this.content, 20 | softWrap: true, 21 | style: TextStyles.commonStyle(1, Colors.black54), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /16/lib/widgets/article_detail/img.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子图片 6 | /// 7 | /// [title]为贴子详情内容 8 | class ArticleDetailImg extends StatelessWidget { 9 | /// 传入的贴子标题 10 | final String articleImage; 11 | 12 | /// 构造函数 13 | const ArticleDetailImg({Key key, this.articleImage}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Image.network( 21 | articleImage, 22 | fit: BoxFit.cover, 23 | width: MediaQuery.of(context).size.width, 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /16/lib/widgets/article_detail/title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子标题 6 | /// 7 | /// [title]为贴子详情内容 8 | class ArticleDetailTitle extends StatelessWidget { 9 | /// 传入的贴子标题 10 | final String title; 11 | 12 | /// 构造函数 13 | const ArticleDetailTitle({Key key, this.title}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Center( 21 | child: Text( 22 | this.title, 23 | softWrap: true, 24 | style: TextStyles.commonStyle(1.2), 25 | ), 26 | )); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /16/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 贴子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /16/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /16/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /16/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /16/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /16/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /18/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /18/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /18/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /18/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /18/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /18/lib/model/new_message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class NewMessageModel with ChangeNotifier { 5 | /// 系统未读新消息数 6 | int newMessageNum; 7 | 8 | /// 构造函数 9 | NewMessageModel({this.newMessageNum}); 10 | 11 | /// 获取未读消息 12 | int get value => newMessageNum; 13 | 14 | /// 设置已经阅读消息 15 | void readNewMessage() { 16 | if (newMessageNum == 0) { 17 | return; 18 | } 19 | newMessageNum = 0; 20 | notifyListeners(); 21 | } 22 | 23 | /// 接口异步返回重新通知 24 | void setNewMessageNum(int unReadNum) { 25 | if (unReadNum == null || unReadNum == 0) { 26 | return; 27 | } 28 | newMessageNum = unReadNum; 29 | notifyListeners(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /18/lib/model/user_info_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/user_info.dart'; 4 | 5 | /// name状态管理模块 6 | class UserInfoModel with ChangeNotifier { 7 | /// 系统用户个人信息 8 | final StructUserInfo myUserInfo; 9 | 10 | /// 构造函数 11 | UserInfoModel({this.myUserInfo}); 12 | 13 | /// 获取用户信息 14 | StructUserInfo get value => myUserInfo; 15 | } 16 | -------------------------------------------------------------------------------- /18/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /18/lib/pages/system_config_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/system_page/switch_card.dart'; 4 | 5 | /// 首页 6 | class SystemConfigPageIndex extends StatelessWidget { 7 | /// 构造函数 8 | const SystemConfigPageIndex(); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | padding: EdgeInsets.all(8), 14 | child: Column( 15 | children: [ 16 | SystemPageSwitchCard(itemDesc: '新消息提醒', switchItem: 'accessMessage'), 17 | SystemPageSwitchCard(itemDesc: '通知显示详情', switchItem: 'tipsDetail'), 18 | SystemPageSwitchCard(itemDesc: '声音', switchItem: 'soundReminder'), 19 | SystemPageSwitchCard(itemDesc: '振动', switchItem: 'vibrationReminder') 20 | ], 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /18/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/user_page/button_list.dart'; 4 | import 'package:two_you_friend/widgets/user_page/header.dart'; 5 | 6 | /// 首页 7 | class UserPageIndex extends StatelessWidget { 8 | /// 构造函数 9 | const UserPageIndex(); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | UserPageHeader(), 16 | Divider(height: 1.0, indent: 70, color: Colors.grey), 17 | Expanded( 18 | child: UserPageButtonList(), 19 | ), 20 | ], 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /18/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /18/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | 19 | /// 将json数据转化为对象数据 20 | StructCommentInfo.fromJson(Map json) 21 | : comment = json['comment'] as String, 22 | userInfo = 23 | StructUserInfo.fromJson(json['userInfo'] as Map); 24 | 25 | /// 将对象转化为json数据 26 | static Map toJson(StructCommentInfo commentInfo) => { 27 | 'comment': commentInfo.comment, 28 | 'userInfo': StructUserInfo.toJson(commentInfo.userInfo) 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /18/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 是否需要默认 Scaffold 15 | final bool needScaffold; 16 | 17 | /// 默认构造函数 18 | const StructRouter( 19 | this.widget, this.entranceIndex, this.params, this.needScaffold); 20 | } 21 | -------------------------------------------------------------------------------- /18/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | 21 | /// 将json数据转化为对象数据 22 | StructUserInfo.fromJson(Map json) 23 | : uid = json['uid'] as String, 24 | headerUrl = json['headerUrl'] as String, 25 | nickName = json['nickName'] as String; 26 | 27 | /// 将对象转化为json数据 28 | static Map toJson(StructUserInfo userInfo) => { 29 | 'uid': userInfo.uid, 30 | 'headerUrl': userInfo.headerUrl, 31 | 'nickName': userInfo.nickName 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /18/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map> cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future> getConfig(String fileName) async { 14 | final jsonString = 15 | await rootBundle.loadString('assets/json/${fileName}.json'); 16 | try { 17 | Map jsonRet = 18 | json.decode(jsonString) as Map; 19 | return jsonRet; 20 | } catch (err) { 21 | print(err); 22 | return null; 23 | } 24 | } 25 | 26 | /// object convert to map data 27 | static Map objectToMap(Object data) { 28 | return jsonDecode(JsonCodec().encode(data)) as Map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /18/lib/util/tools/local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:path_provider/path_provider.dart'; 5 | 6 | /// 本地文件存储,可以存储系统设置相关的数据 7 | class LocalStorage { 8 | /// 将数据保存到文件中 9 | static Future save(String content, String filePath) async { 10 | final directory = await getApplicationDocumentsDirectory(); 11 | 12 | try { 13 | File file = File('${directory.path}/$filePath'); 14 | file.writeAsString(content); 15 | } catch (e) {} 16 | } 17 | 18 | /// 获取文件数据内容 19 | static Future get(String filePath) async { 20 | try { 21 | final directory = await getApplicationDocumentsDirectory(); 22 | 23 | File file = File('${directory.path}/$filePath'); 24 | 25 | bool exist = await file.exists(); 26 | 27 | if (!exist) { 28 | // 判断是否存在文件 29 | return ''; 30 | } 31 | 32 | return file.readAsString(); 33 | } catch (e) { 34 | return ''; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /18/lib/util/tools/time_format.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | /// 时间戳处理 4 | class TimeFormat { 5 | /// 将时间戳转化为字符串 6 | static String tsToTimeString(int timestamp) { 7 | var now = DateTime.now(); 8 | var format = DateFormat('MM月d日 HH:mm'); 9 | var formatYesterday = DateFormat('HH:mm'); 10 | var date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); 11 | var diff = now.difference(date); 12 | var time = ''; 13 | 14 | if (diff.inDays == 0 && diff.inHours == 0 && diff.inMinutes == 0) { 15 | time = diff.inSeconds.toString() + '秒前'; 16 | } else if (diff.inDays == 0 && diff.inHours == 0) { 17 | time = diff.inMinutes.toString() + '分钟前'; 18 | } else if (diff.inDays == 0 && diff.inHours > 0) { 19 | time = diff.inMinutes.toString() + '小时前'; 20 | } else if (diff.inDays < 2 && diff.inDays > 0) { 21 | time = '昨天 ' + formatYesterday.format(date); 22 | } else if (diff.inDays < 5 && diff.inDays > 0) { 23 | time = diff.inDays.toString() + '天前'; 24 | } else { 25 | time = format.format(date); 26 | } 27 | return time; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /18/lib/widgets/article_detail/content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子详情,内容模块 6 | /// 7 | /// [content]为贴子详情内容 8 | class ArticleDetailContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleDetailContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Text( 18 | this.content, 19 | softWrap: true, 20 | style: TextStyles.commonStyle(1, Colors.black54), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /18/lib/widgets/article_detail/img.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 具体的贴子图片 4 | /// 5 | /// [title]为贴子详情内容 6 | class ArticleDetailImg extends StatelessWidget { 7 | /// 传入的贴子标题 8 | final String articleImage; 9 | 10 | /// 构造函数 11 | const ArticleDetailImg({Key key, this.articleImage}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | color: Colors.white, 17 | padding: EdgeInsets.all(8), 18 | child: Image.network( 19 | articleImage, 20 | fit: BoxFit.cover, 21 | width: MediaQuery.of(context).size.width, 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /18/lib/widgets/article_detail/title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子标题 6 | /// 7 | /// [title]为贴子详情内容 8 | class ArticleDetailTitle extends StatelessWidget { 9 | /// 传入的贴子标题 10 | final String title; 11 | 12 | /// 构造函数 13 | const ArticleDetailTitle({Key key, this.title}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Center( 21 | child: Text( 22 | this.title, 23 | softWrap: true, 24 | style: TextStyles.commonStyle(1.2), 25 | ), 26 | )); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /18/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 贴子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /18/lib/widgets/search/content_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/project_router.dart'; 4 | import 'package:two_you_friend/styles/text_syles.dart'; 5 | import 'package:two_you_friend/util/struct/content_detail.dart'; 6 | 7 | /// 头部信息部分 8 | class SearchContentCard extends StatelessWidget { 9 | /// 用户信息 10 | final StructContentDetail contentInfo; 11 | 12 | /// 构造函数 13 | const SearchContentCard({Key key, this.contentInfo}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return FlatButton( 18 | padding: EdgeInsets.all(8), 19 | onPressed: () =>ProjectRouter() 20 | .open(context, "tyfapp://contentpage?articleId=${contentInfo.id}"), 21 | child: Row( 22 | children: [ 23 | Text( 24 | contentInfo.title, 25 | style: TextStyles.commonStyle(1), 26 | ), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /18/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /18/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /18/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /18/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /18/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /19/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /19/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /19/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /19/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /19/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /19/lib/model/new_message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class NewMessageModel with ChangeNotifier { 5 | /// 系统未读新消息数 6 | int newMessageNum; 7 | 8 | /// 构造函数 9 | NewMessageModel({this.newMessageNum}); 10 | 11 | /// 获取未读消息 12 | int get value => newMessageNum; 13 | 14 | /// 设置已经阅读消息 15 | void readNewMessage() { 16 | if (newMessageNum == 0) { 17 | return; 18 | } 19 | newMessageNum = 0; 20 | notifyListeners(); 21 | } 22 | 23 | /// 接口异步返回重新通知 24 | void setNewMessageNum(int unReadNum) { 25 | if (unReadNum == null || unReadNum == 0) { 26 | return; 27 | } 28 | newMessageNum = unReadNum; 29 | notifyListeners(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /19/lib/model/user_info_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/user_info.dart'; 4 | 5 | /// name状态管理模块 6 | class UserInfoModel with ChangeNotifier { 7 | /// 系统用户个人信息 8 | final StructUserInfo myUserInfo; 9 | 10 | /// 构造函数 11 | UserInfoModel({this.myUserInfo}); 12 | 13 | /// 获取用户信息 14 | StructUserInfo get value => myUserInfo; 15 | } 16 | -------------------------------------------------------------------------------- /19/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /19/lib/pages/system_config_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/system_page/switch_card.dart'; 4 | 5 | /// 首页 6 | class SystemConfigPageIndex extends StatelessWidget { 7 | /// 构造函数 8 | const SystemConfigPageIndex(); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | padding: EdgeInsets.all(8), 14 | child: Column( 15 | children: [ 16 | SystemPageSwitchCard(itemDesc: '新消息提醒', switchItem: 'accessMessage'), 17 | SystemPageSwitchCard(itemDesc: '通知显示详情', switchItem: 'tipsDetail'), 18 | SystemPageSwitchCard(itemDesc: '声音', switchItem: 'soundReminder'), 19 | SystemPageSwitchCard(itemDesc: '振动', switchItem: 'vibrationReminder') 20 | ], 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /19/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/user_page/button_list.dart'; 4 | import 'package:two_you_friend/widgets/user_page/header.dart'; 5 | 6 | /// 首页 7 | class UserPageIndex extends StatelessWidget { 8 | /// 构造函数 9 | const UserPageIndex(); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | UserPageHeader(), 16 | Divider(height: 1.0, indent: 70, color: Colors.grey), 17 | Expanded( 18 | child: UserPageButtonList(), 19 | ), 20 | ], 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /19/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /19/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | 19 | /// 将json数据转化为对象数据 20 | StructCommentInfo.fromJson(Map json) 21 | : comment = json['comment'] as String, 22 | userInfo = 23 | StructUserInfo.fromJson(json['userInfo'] as Map); 24 | 25 | /// 将对象转化为json数据 26 | static Map toJson(StructCommentInfo commentInfo) => { 27 | 'comment': commentInfo.comment, 28 | 'userInfo': StructUserInfo.toJson(commentInfo.userInfo) 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /19/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 是否需要默认 Scaffold 15 | final bool needScaffold; 16 | 17 | /// 默认构造函数 18 | const StructRouter( 19 | this.widget, this.entranceIndex, this.params, this.needScaffold); 20 | } 21 | -------------------------------------------------------------------------------- /19/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | 21 | /// 将json数据转化为对象数据 22 | StructUserInfo.fromJson(Map json) 23 | : uid = json['uid'] as String, 24 | headerUrl = json['headerUrl'] as String, 25 | nickName = json['nickName'] as String; 26 | 27 | /// 将对象转化为json数据 28 | static Map toJson(StructUserInfo userInfo) => { 29 | 'uid': userInfo.uid, 30 | 'headerUrl': userInfo.headerUrl, 31 | 'nickName': userInfo.nickName 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /19/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map> cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future> getConfig(String fileName) async { 14 | final jsonString = 15 | await rootBundle.loadString('assets/json/${fileName}.json'); 16 | try { 17 | Map jsonRet = 18 | json.decode(jsonString) as Map; 19 | return jsonRet; 20 | } catch (err) { 21 | print(err); 22 | return null; 23 | } 24 | } 25 | 26 | /// object convert to map data 27 | static Map objectToMap(Object data) { 28 | return jsonDecode(JsonCodec().encode(data)) as Map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /19/lib/util/tools/local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:pedantic/pedantic.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | 7 | /// 本地文件存储,可以存储系统设置相关的数据 8 | class LocalStorage { 9 | /// 将数据保存到文件中 10 | static Future save(String content, String filePath) async { 11 | final directory = await getApplicationDocumentsDirectory(); 12 | 13 | try { 14 | File file = File('${directory.path}/$filePath'); 15 | unawaited(file.writeAsString(content)); 16 | } catch (e) { 17 | print(e); 18 | } 19 | } 20 | 21 | /// 获取文件数据内容 22 | static Future get(String filePath) async { 23 | try { 24 | final directory = await getApplicationDocumentsDirectory(); 25 | 26 | File file = File('${directory.path}/$filePath'); 27 | 28 | bool exist = await file.exists(); 29 | 30 | if (!exist) { 31 | // 判断是否存在文件 32 | return ''; 33 | } 34 | 35 | return file.readAsString(); 36 | } catch (e) { 37 | return ''; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /19/lib/util/tools/time_format.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | /// 时间戳处理 4 | class TimeFormat { 5 | /// 将时间戳转化为字符串 6 | static String tsToTimeString(int timestamp) { 7 | var now = DateTime.now(); 8 | var format = DateFormat('MM月d日 HH:mm'); 9 | var formatYesterday = DateFormat('HH:mm'); 10 | var date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); 11 | var diff = now.difference(date); 12 | var time = ''; 13 | 14 | if (diff.inDays == 0 && diff.inHours == 0 && diff.inMinutes == 0) { 15 | time = diff.inSeconds.toString() + '秒前'; 16 | } else if (diff.inDays == 0 && diff.inHours == 0) { 17 | time = diff.inMinutes.toString() + '分钟前'; 18 | } else if (diff.inDays == 0 && diff.inHours > 0) { 19 | time = diff.inMinutes.toString() + '小时前'; 20 | } else if (diff.inDays < 2 && diff.inDays > 0) { 21 | time = '昨天 ' + formatYesterday.format(date); 22 | } else if (diff.inDays < 5 && diff.inDays > 0) { 23 | time = diff.inDays.toString() + '天前'; 24 | } else { 25 | time = format.format(date); 26 | } 27 | return time; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /19/lib/widgets/article_detail/content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子详情,内容模块 6 | /// 7 | /// [content]为贴子详情内容 8 | class ArticleDetailContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleDetailContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Text( 18 | this.content, 19 | softWrap: true, 20 | style: TextStyles.commonStyle(1, Colors.black54), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /19/lib/widgets/article_detail/img.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 具体的贴子图片 4 | /// 5 | /// [title]为贴子详情内容 6 | class ArticleDetailImg extends StatelessWidget { 7 | /// 传入的贴子标题 8 | final String articleImage; 9 | 10 | /// 构造函数 11 | const ArticleDetailImg({Key key, this.articleImage}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | color: Colors.white, 17 | padding: EdgeInsets.all(8), 18 | child: Image.network( 19 | articleImage, 20 | fit: BoxFit.cover, 21 | width: MediaQuery.of(context).size.width, 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /19/lib/widgets/article_detail/title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子标题 6 | /// 7 | /// [title]为贴子详情内容 8 | class ArticleDetailTitle extends StatelessWidget { 9 | /// 传入的贴子标题 10 | final String title; 11 | 12 | /// 构造函数 13 | const ArticleDetailTitle({Key key, this.title}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Center( 21 | child: Text( 22 | this.title, 23 | softWrap: true, 24 | style: TextStyles.commonStyle(1.2), 25 | ), 26 | )); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /19/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 贴子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /19/lib/widgets/search/content_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/project_router.dart'; 4 | import 'package:two_you_friend/styles/text_syles.dart'; 5 | import 'package:two_you_friend/util/struct/content_detail.dart'; 6 | 7 | /// 头部信息部分 8 | class SearchContentCard extends StatelessWidget { 9 | /// 用户信息 10 | final StructContentDetail contentInfo; 11 | 12 | /// 构造函数 13 | const SearchContentCard({Key key, this.contentInfo}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return FlatButton( 18 | padding: EdgeInsets.all(8), 19 | onPressed: () =>ProjectRouter() 20 | .open(context, "tyfapp://contentpage?articleId=${contentInfo.id}"), 21 | child: Row( 22 | children: [ 23 | Text( 24 | contentInfo.title, 25 | style: TextStyles.commonStyle(1), 26 | ), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /19/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /19/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /19/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /19/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /19/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /20/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /20/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /20/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /20/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /20/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /20/lib/model/new_message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class NewMessageModel with ChangeNotifier { 5 | /// 系统未读新消息数 6 | int newMessageNum; 7 | 8 | /// 构造函数 9 | NewMessageModel({this.newMessageNum}); 10 | 11 | /// 获取未读消息 12 | int get value => newMessageNum; 13 | 14 | /// 设置已经阅读消息 15 | void readNewMessage() { 16 | if (newMessageNum == 0) { 17 | return; 18 | } 19 | newMessageNum = 0; 20 | notifyListeners(); 21 | } 22 | 23 | /// 接口异步返回重新通知 24 | void setNewMessageNum(int unReadNum) { 25 | if (unReadNum == null || unReadNum == 0) { 26 | return; 27 | } 28 | newMessageNum = unReadNum; 29 | notifyListeners(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /20/lib/model/user_info_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/user_info.dart'; 4 | 5 | /// name状态管理模块 6 | class UserInfoModel with ChangeNotifier { 7 | /// 系统用户个人信息 8 | final StructUserInfo myUserInfo; 9 | 10 | /// 构造函数 11 | UserInfoModel({this.myUserInfo}); 12 | 13 | /// 获取用户信息 14 | StructUserInfo get value => myUserInfo; 15 | } 16 | -------------------------------------------------------------------------------- /20/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /20/lib/pages/system_config_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/system_page/switch_card.dart'; 4 | 5 | /// 首页 6 | class SystemConfigPageIndex extends StatelessWidget { 7 | /// 构造函数 8 | const SystemConfigPageIndex(); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | padding: EdgeInsets.all(8), 14 | child: Column( 15 | children: [ 16 | SystemPageSwitchCard(itemDesc: '新消息提醒', switchItem: 'accessMessage'), 17 | SystemPageSwitchCard(itemDesc: '通知显示详情', switchItem: 'tipsDetail'), 18 | SystemPageSwitchCard(itemDesc: '声音', switchItem: 'soundReminder'), 19 | SystemPageSwitchCard(itemDesc: '振动', switchItem: 'vibrationReminder') 20 | ], 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /20/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/user_page/button_list.dart'; 4 | import 'package:two_you_friend/widgets/user_page/header.dart'; 5 | 6 | /// 首页 7 | class UserPageIndex extends StatelessWidget { 8 | /// 构造函数 9 | const UserPageIndex(); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | UserPageHeader(), 16 | Divider(height: 1.0, indent: 70, color: Colors.grey), 17 | Expanded( 18 | child: UserPageButtonList(), 19 | ), 20 | ], 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /20/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /20/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | 19 | /// 将json数据转化为对象数据 20 | StructCommentInfo.fromJson(Map json) 21 | : comment = json['comment'] as String, 22 | userInfo = 23 | StructUserInfo.fromJson(json['userInfo'] as Map); 24 | 25 | /// 将对象转化为json数据 26 | static Map toJson(StructCommentInfo commentInfo) => { 27 | 'comment': commentInfo.comment, 28 | 'userInfo': StructUserInfo.toJson(commentInfo.userInfo) 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /20/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 是否需要默认 Scaffold 15 | final bool needScaffold; 16 | 17 | /// 默认构造函数 18 | const StructRouter( 19 | this.widget, this.entranceIndex, this.params, this.needScaffold); 20 | } 21 | -------------------------------------------------------------------------------- /20/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | 21 | /// 将json数据转化为对象数据 22 | StructUserInfo.fromJson(Map json) 23 | : uid = json['uid'] as String, 24 | headerUrl = json['headerUrl'] as String, 25 | nickName = json['nickName'] as String; 26 | 27 | /// 将对象转化为json数据 28 | static Map toJson(StructUserInfo userInfo) => { 29 | 'uid': userInfo.uid, 30 | 'headerUrl': userInfo.headerUrl, 31 | 'nickName': userInfo.nickName 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /20/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map> cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future> getConfig(String fileName) async { 14 | final jsonString = 15 | await rootBundle.loadString('assets/json/${fileName}.json'); 16 | try { 17 | Map jsonRet = 18 | json.decode(jsonString) as Map; 19 | return jsonRet; 20 | } catch (err) { 21 | print(err); 22 | return null; 23 | } 24 | } 25 | 26 | /// object convert to map data 27 | static Map objectToMap(Object data) { 28 | return jsonDecode(JsonCodec().encode(data)) as Map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /20/lib/util/tools/local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:pedantic/pedantic.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | 7 | /// 本地文件存储,可以存储系统设置相关的数据 8 | class LocalStorage { 9 | /// 将数据保存到文件中 10 | static Future save(String content, String filePath) async { 11 | final directory = await getApplicationDocumentsDirectory(); 12 | 13 | try { 14 | File file = File('${directory.path}/$filePath'); 15 | unawaited(file.writeAsString(content)); 16 | } catch (e) { 17 | print(e); 18 | } 19 | } 20 | 21 | /// 获取文件数据内容 22 | static Future get(String filePath) async { 23 | try { 24 | final directory = await getApplicationDocumentsDirectory(); 25 | 26 | File file = File('${directory.path}/$filePath'); 27 | 28 | bool exist = await file.exists(); 29 | 30 | if (!exist) { 31 | // 判断是否存在文件 32 | return ''; 33 | } 34 | 35 | return file.readAsString(); 36 | } catch (e) { 37 | return ''; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /20/lib/util/tools/time_format.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | /// 时间戳处理 4 | class TimeFormat { 5 | /// 将时间戳转化为字符串 6 | static String tsToTimeString(int timestamp) { 7 | var now = DateTime.now(); 8 | var format = DateFormat('MM月d日 HH:mm'); 9 | var formatYesterday = DateFormat('HH:mm'); 10 | var date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); 11 | var diff = now.difference(date); 12 | var time = ''; 13 | 14 | if (diff.inDays == 0 && diff.inHours == 0 && diff.inMinutes == 0) { 15 | time = diff.inSeconds.toString() + '秒前'; 16 | } else if (diff.inDays == 0 && diff.inHours == 0) { 17 | time = diff.inMinutes.toString() + '分钟前'; 18 | } else if (diff.inDays == 0 && diff.inHours > 0) { 19 | time = diff.inMinutes.toString() + '小时前'; 20 | } else if (diff.inDays < 2 && diff.inDays > 0) { 21 | time = '昨天 ' + formatYesterday.format(date); 22 | } else if (diff.inDays < 5 && diff.inDays > 0) { 23 | time = diff.inDays.toString() + '天前'; 24 | } else { 25 | time = format.format(date); 26 | } 27 | return time; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /20/lib/widgets/article_detail/content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子详情,内容模块 6 | /// 7 | /// [content]为贴子详情内容 8 | class ArticleDetailContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleDetailContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Text( 18 | this.content, 19 | softWrap: true, 20 | style: TextStyles.commonStyle(1, Colors.black54), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /20/lib/widgets/article_detail/img.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 具体的贴子图片 4 | /// 5 | /// [title]为贴子详情内容 6 | class ArticleDetailImg extends StatelessWidget { 7 | /// 传入的贴子标题 8 | final String articleImage; 9 | 10 | /// 构造函数 11 | const ArticleDetailImg({Key key, this.articleImage}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | color: Colors.white, 17 | padding: EdgeInsets.all(8), 18 | child: Image.network( 19 | articleImage, 20 | fit: BoxFit.cover, 21 | width: MediaQuery.of(context).size.width, 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /20/lib/widgets/article_detail/title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子标题 6 | /// 7 | /// [title]为贴子详情内容 8 | class ArticleDetailTitle extends StatelessWidget { 9 | /// 传入的贴子标题 10 | final String title; 11 | 12 | /// 构造函数 13 | const ArticleDetailTitle({Key key, this.title}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Center( 21 | child: Text( 22 | this.title, 23 | softWrap: true, 24 | style: TextStyles.commonStyle(1.2), 25 | ), 26 | )); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /20/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 贴子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /20/lib/widgets/search/content_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/project_router.dart'; 4 | import 'package:two_you_friend/styles/text_syles.dart'; 5 | import 'package:two_you_friend/util/struct/content_detail.dart'; 6 | 7 | /// 头部信息部分 8 | class SearchContentCard extends StatelessWidget { 9 | /// 用户信息 10 | final StructContentDetail contentInfo; 11 | 12 | /// 构造函数 13 | const SearchContentCard({Key key, this.contentInfo}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return FlatButton( 18 | padding: EdgeInsets.all(8), 19 | onPressed: () =>ProjectRouter() 20 | .open(context, "tyfapp://contentpage?articleId=${contentInfo.id}"), 21 | child: Row( 22 | children: [ 23 | Text( 24 | contentInfo.title, 25 | style: TextStyles.commonStyle(1), 26 | ), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /20/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /20/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /20/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /20/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /20/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /21/README.md: -------------------------------------------------------------------------------- 1 | # two_you_friend 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /21/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.1.8.0.yaml 2 | analyzer: 3 | strong-mode: 4 | implicit-casts: false 5 | 6 | linter: 7 | rules: 8 | # STYLE 9 | - camel_case_types 10 | - camel_case_extensions 11 | - file_names 12 | - non_constant_identifier_names 13 | - constant_identifier_names # prefer 14 | - directives_ordering 15 | - lines_longer_than_80_chars # avoid 16 | 17 | # DOCUMENTATION 18 | - package_api_docs # prefer 19 | - public_member_api_docs # prefer 20 | -------------------------------------------------------------------------------- /21/format_check.bat: -------------------------------------------------------------------------------- 1 | 2 | dartfmt -w --fix lib/ 3 | 4 | dartanalyzer lib 5 | 6 | flutter test -------------------------------------------------------------------------------- /21/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 代码美化 4 | dartfmt -w --fix lib/ 5 | 6 | # 代码规范检查 7 | dartanalyzer lib 8 | 9 | # 单元测试通过 10 | flutter test -------------------------------------------------------------------------------- /21/lib/api/content/comment.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /21/lib/model/new_message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// name状态管理模块 4 | class NewMessageModel with ChangeNotifier { 5 | /// 系统未读新消息数 6 | int newMessageNum; 7 | 8 | /// 构造函数 9 | NewMessageModel({this.newMessageNum}); 10 | 11 | /// 获取未读消息 12 | int get value => newMessageNum; 13 | 14 | /// 设置已经阅读消息 15 | void readNewMessage() { 16 | if (newMessageNum == 0) { 17 | return; 18 | } 19 | newMessageNum = 0; 20 | notifyListeners(); 21 | } 22 | 23 | /// 接口异步返回重新通知 24 | void setNewMessageNum(int unReadNum) { 25 | if (unReadNum == null || unReadNum == 0) { 26 | return; 27 | } 28 | newMessageNum = unReadNum; 29 | notifyListeners(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /21/lib/model/user_info_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/user_info.dart'; 4 | 5 | /// name状态管理模块 6 | class UserInfoModel with ChangeNotifier { 7 | /// 系统用户个人信息 8 | final StructUserInfo myUserInfo; 9 | 10 | /// 构造函数 11 | UserInfoModel({this.myUserInfo}); 12 | 13 | /// 获取用户信息 14 | StructUserInfo get value => myUserInfo; 15 | } 16 | -------------------------------------------------------------------------------- /21/lib/pages/common/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | 4 | /// 通用跳转逻辑 5 | class CommonWebViewPage extends StatelessWidget { 6 | /// url 地址 7 | final String url; 8 | 9 | /// 构造函数 10 | CommonWebViewPage({Key key, this.url}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return WebviewScaffold( 15 | url: url, 16 | appBar: AppBar( 17 | backgroundColor: Colors.blue, 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /21/lib/pages/system_config_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/system_page/switch_card.dart'; 4 | 5 | /// 首页 6 | class SystemConfigPageIndex extends StatelessWidget { 7 | /// 构造函数 8 | const SystemConfigPageIndex(); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | padding: EdgeInsets.all(8), 14 | child: Column( 15 | children: [ 16 | SystemPageSwitchCard(itemDesc: '新消息提醒', switchItem: 'accessMessage'), 17 | SystemPageSwitchCard(itemDesc: '通知显示详情', switchItem: 'tipsDetail'), 18 | SystemPageSwitchCard(itemDesc: '声音', switchItem: 'soundReminder'), 19 | SystemPageSwitchCard(itemDesc: '振动', switchItem: 'vibrationReminder') 20 | ], 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /21/lib/pages/user_page/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/widgets/user_page/button_list.dart'; 4 | import 'package:two_you_friend/widgets/user_page/header.dart'; 5 | 6 | /// 首页 7 | class UserPageIndex extends StatelessWidget { 8 | /// 构造函数 9 | const UserPageIndex(); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | UserPageHeader(), 16 | Divider(height: 1.0, indent: 70, color: Colors.grey), 17 | Expanded( 18 | child: UserPageButtonList(), 19 | ), 20 | ], 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /21/lib/styles/text_syles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 文本样式相关的类 4 | /// 5 | /// 获取文本样式内容,各类文本相关的都会包含在此,目的是后续修改字体,修改默认字体大小,可以统一 6 | class TextStyles { 7 | /// 默认字体大小 8 | static double baseFontSize = 18.0; 9 | 10 | /// 主页内容的bottom bar下的样式 11 | static TextStyle commonStyle( 12 | [double multipleFontSize = 1, Color myColor = Colors.lightBlueAccent]) { 13 | return TextStyle( 14 | color: myColor, 15 | fontSize: baseFontSize * multipleFontSize, 16 | letterSpacing: 1, 17 | wordSpacing: 2, 18 | height: 1.2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /21/lib/util/struct/comment_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:two_you_friend/util/struct/user_info.dart'; 2 | 3 | /// 用户信息 4 | /// 5 | /// { 6 | /// "userInfo" : "StructUserInfo", 7 | /// "comment" : "string" 8 | /// } 9 | class StructCommentInfo { 10 | /// 用户的昵称 11 | final StructUserInfo userInfo; 12 | 13 | /// 用户头像信息 14 | final String comment; 15 | 16 | /// 构造函数 17 | const StructCommentInfo(this.userInfo, this.comment); 18 | 19 | /// 将json数据转化为对象数据 20 | StructCommentInfo.fromJson(Map json) 21 | : comment = json['comment'] as String, 22 | userInfo = 23 | StructUserInfo.fromJson(json['userInfo'] as Map); 24 | 25 | /// 将对象转化为json数据 26 | static Map toJson(StructCommentInfo commentInfo) => { 27 | 'comment': commentInfo.comment, 28 | 'userInfo': StructUserInfo.toJson(commentInfo.userInfo) 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /21/lib/util/struct/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// router 配置数据结构 4 | class StructRouter { 5 | /// 组件 6 | final Widget widget; 7 | 8 | /// 首页入口index 9 | final int entranceIndex; 10 | 11 | /// 组件参数列表 12 | final List params; 13 | 14 | /// 是否需要默认 Scaffold 15 | final bool needScaffold; 16 | 17 | /// 默认构造函数 18 | const StructRouter( 19 | this.widget, this.entranceIndex, this.params, this.needScaffold); 20 | } 21 | -------------------------------------------------------------------------------- /21/lib/util/struct/user_info.dart: -------------------------------------------------------------------------------- 1 | /// 用户信息 2 | /// 3 | /// { 4 | /// "nickname" : "string", 5 | /// "headerUrl" : "string", 6 | /// "uid" : "string" 7 | /// } 8 | class StructUserInfo { 9 | /// 标题 10 | final String nickName; 11 | 12 | /// 简要 13 | final String headerUrl; 14 | 15 | /// 主要内容 16 | final String uid; 17 | 18 | /// 默认构造函数 19 | const StructUserInfo(this.uid, this.nickName, this.headerUrl); 20 | 21 | /// 将json数据转化为对象数据 22 | StructUserInfo.fromJson(Map json) 23 | : uid = json['uid'] as String, 24 | headerUrl = json['headerUrl'] as String, 25 | nickName = json['nickName'] as String; 26 | 27 | /// 将对象转化为json数据 28 | static Map toJson(StructUserInfo userInfo) => { 29 | 'uid': userInfo.uid, 30 | 'headerUrl': userInfo.headerUrl, 31 | 'nickName': userInfo.nickName 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /21/lib/util/tools/json_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart' show rootBundle; 4 | 5 | /// 缓存配置信息,避免多次读取文件 6 | const Map> cache = {}; 7 | 8 | /// 获取 json 配置文件 9 | /// 10 | /// 在使用该方法前,需要在 pubspec.yaml 中增加 assets 的配置引入 11 | class JsonConfig { 12 | /// 读取 json 配置文件,并解析返回 13 | static Future> getConfig(String fileName) async { 14 | final jsonString = 15 | await rootBundle.loadString('assets/json/${fileName}.json'); 16 | try { 17 | Map jsonRet = 18 | json.decode(jsonString) as Map; 19 | return jsonRet; 20 | } catch (err) { 21 | print(err); 22 | return null; 23 | } 24 | } 25 | 26 | /// object convert to map data 27 | static Map objectToMap(Object data) { 28 | return jsonDecode(JsonCodec().encode(data)) as Map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /21/lib/util/tools/local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:pedantic/pedantic.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | 7 | /// 本地文件存储,可以存储系统设置相关的数据 8 | class LocalStorage { 9 | /// 将数据保存到文件中 10 | static Future save(String content, String filePath) async { 11 | final directory = await getApplicationDocumentsDirectory(); 12 | 13 | try { 14 | File file = File('${directory.path}/$filePath'); 15 | unawaited(file.writeAsString(content)); 16 | } catch (e) { 17 | print(e); 18 | } 19 | } 20 | 21 | /// 获取文件数据内容 22 | static Future get(String filePath) async { 23 | try { 24 | final directory = await getApplicationDocumentsDirectory(); 25 | 26 | File file = File('${directory.path}/$filePath'); 27 | 28 | bool exist = await file.exists(); 29 | 30 | if (!exist) { 31 | // 判断是否存在文件 32 | return ''; 33 | } 34 | 35 | return file.readAsString(); 36 | } catch (e) { 37 | return ''; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /21/lib/util/tools/time_format.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | /// 时间戳处理 4 | class TimeFormat { 5 | /// 将时间戳转化为字符串 6 | static String tsToTimeString(int timestamp) { 7 | var now = DateTime.now(); 8 | var format = DateFormat('MM月d日 HH:mm'); 9 | var formatYesterday = DateFormat('HH:mm'); 10 | var date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); 11 | var diff = now.difference(date); 12 | var time = ''; 13 | 14 | if (diff.inDays == 0 && diff.inHours == 0 && diff.inMinutes == 0) { 15 | time = diff.inSeconds.toString() + '秒前'; 16 | } else if (diff.inDays == 0 && diff.inHours == 0) { 17 | time = diff.inMinutes.toString() + '分钟前'; 18 | } else if (diff.inDays == 0 && diff.inHours > 0) { 19 | time = diff.inMinutes.toString() + '小时前'; 20 | } else if (diff.inDays < 2 && diff.inDays > 0) { 21 | time = '昨天 ' + formatYesterday.format(date); 22 | } else if (diff.inDays < 5 && diff.inDays > 0) { 23 | time = diff.inDays.toString() + '天前'; 24 | } else { 25 | time = format.format(date); 26 | } 27 | return time; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /21/lib/widgets/article_detail/content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子详情,内容模块 6 | /// 7 | /// [content]为贴子详情内容 8 | class ArticleDetailContent extends StatelessWidget { 9 | /// 传入的用户信息 10 | final String content; 11 | 12 | /// 构造函数 13 | const ArticleDetailContent({Key key, this.content}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Text( 18 | this.content, 19 | softWrap: true, 20 | style: TextStyles.commonStyle(1, Colors.black54), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /21/lib/widgets/article_detail/img.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 具体的贴子图片 4 | /// 5 | /// [title]为贴子详情内容 6 | class ArticleDetailImg extends StatelessWidget { 7 | /// 传入的贴子标题 8 | final String articleImage; 9 | 10 | /// 构造函数 11 | const ArticleDetailImg({Key key, this.articleImage}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | color: Colors.white, 17 | padding: EdgeInsets.all(8), 18 | child: Image.network( 19 | articleImage, 20 | fit: BoxFit.cover, 21 | width: MediaQuery.of(context).size.width, 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /21/lib/widgets/article_detail/title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/styles/text_syles.dart'; 4 | 5 | /// 具体的贴子标题 6 | /// 7 | /// [title]为贴子详情内容 8 | class ArticleDetailTitle extends StatelessWidget { 9 | /// 传入的贴子标题 10 | final String title; 11 | 12 | /// 构造函数 13 | const ArticleDetailTitle({Key key, this.title}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | color: Colors.white, 19 | padding: EdgeInsets.all(8), 20 | child: Center( 21 | child: Text( 22 | this.title, 23 | softWrap: true, 24 | style: TextStyles.commonStyle(1.2), 25 | ), 26 | )); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /21/lib/widgets/common/banner_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// banner 展示组件 4 | /// 5 | /// 只需要传入需要展示的[bannerImage] 6 | class BannerInfo extends StatelessWidget { 7 | /// 贴子标题 8 | final String bannerImage; 9 | 10 | /// 构造函数 11 | const BannerInfo({Key key, this.bannerImage}) : super(key: key); 12 | 13 | /// 左侧的标题和标题描述组件 14 | Widget getLeftInfo() { 15 | return Row( 16 | children: [], 17 | ); 18 | } 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Row( 23 | children: [ 24 | Image.network( 25 | bannerImage, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | fit: BoxFit.cover, 29 | ), 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /21/lib/widgets/search/content_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:two_you_friend/project_router.dart'; 4 | import 'package:two_you_friend/styles/text_syles.dart'; 5 | import 'package:two_you_friend/util/struct/content_detail.dart'; 6 | 7 | /// 头部信息部分 8 | class SearchContentCard extends StatelessWidget { 9 | /// 用户信息 10 | final StructContentDetail contentInfo; 11 | 12 | /// 构造函数 13 | const SearchContentCard({Key key, this.contentInfo}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return FlatButton( 18 | padding: EdgeInsets.all(8), 19 | onPressed: () => ProjectRouter() 20 | .open(context, "tyfapp://contentpage?articleId=${contentInfo.id}"), 21 | child: Row( 22 | children: [ 23 | Text( 24 | contentInfo.title, 25 | style: TextStyles.commonStyle(1), 26 | ), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /21/test/pages/common/web_view_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/common/web_view_page.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/common/web_view_page.dart', (WidgetTester tester) async { 9 | final Widget testWidgets = CommonWebViewPage(url: 'http://www.qq.com'); 10 | await tester.pumpWidget( 11 | new MaterialApp( 12 | home: testWidgets 13 | ) 14 | ); 15 | 16 | expect(find.byWidget(testWidgets), findsOneWidget); 17 | }); 18 | } -------------------------------------------------------------------------------- /21/test/pages/entrance_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/entrance.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/entrance.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /21/test/pages/home_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/home_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/home_page/index.dart', (WidgetTester tester) async { 9 | 10 | }); 11 | } -------------------------------------------------------------------------------- /21/test/pages/user_page/index_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:two_you_friend/pages/user_page/index.dart'; 5 | 6 | // @todo 7 | void main() { 8 | testWidgets('test two_you_friend/pages/user_page/index.dart', (WidgetTester tester) async { 9 | }); 10 | } -------------------------------------------------------------------------------- /21/test/util/struct/router_struct_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:two_you_friend/util/struct/router.dart'; 4 | 5 | // @todo 6 | void main() { 7 | // final RouterStruct routerStruct = RouterStruct(@todo); 8 | 9 | test('test two_you_friend/struct/project_router.dart', () { 10 | // expect(routerStruct.todo, @todo); 11 | }); 12 | 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### flutter 课程对应的代码 2 | #### 目录说明 3 | 文件夹序号,例如 02 代表的是具体的专栏序号。如果里面只有一份代码,将没有二级目录。如果存在多份代码,将按照专栏中的二级标题命名。 4 | #### 运行说明 5 | 1. 创建项目,项目名为two_you_friend(务必为这个) 6 | 2. 将代码覆盖lib下路径下的代码 7 | 3. 将根目录下的analysis_options.yaml、format_check.sh、pubspec.yaml拷贝进去覆盖 8 | 4. 执行flutter pub get更新本地包文件 9 | 5. 启动运行项目 10 | 11 | #### 特别注意 12 | 由于 flutter 已经更新到 2.0 版本,因此其中做了适配,只能运行在 2.0 以上的版本 13 | --------------------------------------------------------------------------------